Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 15 Jan 2015 14:40:29 +0100
changeset 237184 b60c4d96e3ef3e879ae8ffe532733f7248a15eb5
parent 237159 dfaf13cfe206f868bdc51c7e29c499e9344150e3 (current diff)
parent 237183 206bf1a98cd7a032fa6004298a68b48b51327ba6 (diff)
child 237185 16bae0d7acb37dfa53569ef33e8695e9a1bfb0e6
push id431
push usernalexander@mozilla.com
push dateThu, 15 Jan 2015 22:02:12 +0000
milestone38.0a1
Merge mozilla-central to fx-team
browser/config/mozconfigs/linux64/debug-nonunified
browser/config/mozconfigs/linux64/nightly-nonunified
browser/config/mozconfigs/macosx-universal/nightly-nonunified
browser/config/mozconfigs/macosx64/debug-nonunified
browser/config/mozconfigs/win32/debug-nonunified
browser/config/mozconfigs/win32/nightly-nonunified
browser/config/mozconfigs/win64/debug-nonunified
browser/config/mozconfigs/win64/nightly-nonunified
dom/canvas/test/reftest/colors-half-alpha.png
dom/canvas/test/reftest/colors.png
dom/canvas/test/reftest/half-colors-half-alpha.png
dom/canvas/test/reftest/half-colors.png
dom/canvas/test/reftest/webgl-color-alpha-test.html
dom/canvas/test/reftest/webgl-orientation-test.html
dom/canvas/test/reftest/white-top-left.png
mobile/android/config/mozconfigs/android-api-10/debug-nonunified
mobile/android/config/mozconfigs/android-api-10/nightly-nonunified
mobile/android/config/mozconfigs/android-api-11/debug-nonunified
mobile/android/config/mozconfigs/android-api-11/nightly-nonunified
mobile/android/config/mozconfigs/android/debug-nonunified
mobile/android/config/mozconfigs/android/nightly-nonunified
security/pkix/include/pkix/nullptr.h
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -1014,25 +1014,30 @@ GetProxy(AtkObject* aObj)
 
 AtkObject*
 GetWrapperFor(ProxyAccessible* aProxy)
 {
   return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
 }
 
 static uint16_t
-GetInterfacesForProxy(ProxyAccessible* aProxy)
+GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
-  return MAI_INTERFACE_COMPONENT;
+  uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
+  if (aInterfaces & Interfaces::HYPERTEXT)
+    interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT)
+        | (1 << MAI_INTERFACE_EDITABLE_TEXT);
+
+  return interfaces;
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible* aProxy)
+a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
-  GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
+  GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy, aInterfaces));
   NS_ASSERTION(type, "why don't we have a type!");
 
   AtkObject* obj =
     reinterpret_cast<AtkObject *>
     (g_object_new(type, nullptr));
   if (!obj)
     return;
 
--- a/accessible/atk/nsMaiInterfaceText.cpp
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -4,16 +4,17 @@
  * 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 "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessible-inl.h"
 #include "nsMai.h"
+#include "ProxyAccessible.h"
 
 #include "nsIAccessibleTypes.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
@@ -105,31 +106,33 @@ ConvertTexttoAsterisks(AccessibleWrap* a
 }
 
 extern "C" {
 
 static gchar*
 getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
+  nsAutoString autoStr;
+  if (accWrap) {
+    HyperTextAccessible* text = accWrap->AsHyperText();
+    if (!text || !text->IsTextRole())
+      return nullptr;
 
-  HyperTextAccessible* text = accWrap->AsHyperText();
-  if (!text || !text->IsTextRole())
-    return nullptr;
-
-    nsAutoString autoStr;
     text->TextSubstring(aStartOffset, aEndOffset, autoStr);
 
     ConvertTexttoAsterisks(accWrap, autoStr);
-    NS_ConvertUTF16toUTF8 cautoStr(autoStr);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    proxy->TextSubstring(aStartOffset, aEndOffset, autoStr);
+  }
 
-    //copy and return, libspi will free it.
-    return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
+  NS_ConvertUTF16toUTF8 cautoStr(autoStr);
+
+  //copy and return, libspi will free it.
+  return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gchar*
 getTextAfterOffsetCB(AtkText *aText, gint aOffset,
                      AtkTextBoundary aBoundaryType,
                      gint *aStartOffset, gint *aEndOffset)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -544,10 +544,10 @@ DocManager::RemoteDocAdded(DocAccessible
   if (!sRemoteDocuments) {
     sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
     ClearOnShutdown(&sRemoteDocuments);
   }
 
   MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc),
       "How did we already have the doc!");
   sRemoteDocuments->AppendElement(aDoc);
-  ProxyCreated(aDoc);
+  ProxyCreated(aDoc, 0);
 }
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -50,17 +50,17 @@ void PlatformInit();
  * Note this is called before internal accessibility support is shutdown.
  */
 void PlatformShutdown();
 
 /**
  * called when a new ProxyAccessible is created, so the platform may setup a
  * wrapper for it, or take other action.
  */
-void ProxyCreated(ProxyAccessible*);
+void ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces);
 
 /**
  * Called just before a ProxyAccessible is destroyed so its wrapper can be
  * disposed of and other action taken.
  */
 void ProxyDestroyed(ProxyAccessible*);
 
 /**
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -2,37 +2,49 @@
 /* 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 "DocAccessibleChild.h"
 
 #include "Accessible-inl.h"
+#include "ProxyAccessible.h"
 
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 namespace mozilla {
 namespace a11y {
 
-void
+static uint32_t
+InterfacesFor(Accessible* aAcc)
+{
+  uint32_t interfaces = 0;
+  if (aAcc->IsHyperText() && aAcc->AsHyperText()->IsTextRole())
+    interfaces |= Interfaces::HYPERTEXT;
+
+  return interfaces;
+}
+
+static void
 SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
 {
   uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
   uint32_t role = aRoot->Role();
   uint32_t childCount = aRoot->ChildCount();
+  uint32_t interfaces = InterfacesFor(aRoot);
 
   // OuterDocAccessibles are special because we don't want to serialize the
   // child doc here, we'll call PDocAccessibleConstructor in
   // NotificationController.
   if (childCount == 1 && aRoot->GetChildAt(0)->IsDoc())
     childCount = 0;
 
-  aTree.AppendElement(AccessibleData(id, role, childCount));
+  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces));
   for (uint32_t i = 0; i < childCount; i++)
     SerializeTree(aRoot->GetChildAt(i), aTree);
 }
 
 void
 DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
@@ -112,10 +124,24 @@ DocAccessibleChild::RecvAttributes(const
     rv = propElem->GetValue(value);
     NS_ENSURE_SUCCESS(rv, false);
 
     aAttributes->AppendElement(Attribute(name, value));
     }
 
   return true;
 }
+
+bool
+DocAccessibleChild::RecvTextSubstring(const uint64_t& aID,
+                                      const int32_t& aStartOffset,
+                                      const int32_t& aEndOffset,
+                                      nsString* aText)
+{
+  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  if (!acc || !acc->IsHyperText())
+    return false;
+
+  acc->AsHyperText()->TextSubstring(aStartOffset, aEndOffset, *aText);
+  return true;
 }
 }
+}
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -44,16 +44,20 @@ public:
   virtual bool RecvName(const uint64_t& aID, nsString* aName) MOZ_OVERRIDE;
 
   /*
    * Get the description for the accessible with given id.
    */
   virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE;
 
   virtual bool RecvAttributes(const uint64_t& aID, nsTArray<Attribute> *aAttributes) MOZ_OVERRIDE;
+  virtual bool RecvTextSubstring(const uint64_t& aID,
+                                 const int32_t& aStartOffset,
+                                 const int32_t& aEndOffset, nsString* aText)
+    MOZ_OVERRIDE;
 
 private:
   DocAccessible* mDoc;
 };
 
 }
 }
 
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocAccessibleParent.h"
 #include "nsAutoPtr.h"
 #include "mozilla/a11y/Platform.h"
+#include "ProxyAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
   if (aData.NewTree().IsEmpty()) {
@@ -69,17 +70,17 @@ DocAccessibleParent::AddSubtree(ProxyAcc
     return 0;
   }
 
   auto role = static_cast<a11y::role>(newChild.Role());
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role);
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
-  ProxyCreated(newProxy);
+  ProxyCreated(newProxy, newChild.Interfaces());
 
   uint32_t accessibles = 1;
   uint32_t kids = newChild.ChildrenCount();
   for (uint32_t i = 0; i < kids; i++) {
     uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
     if (!consumed)
       return 0;
 
@@ -137,32 +138,34 @@ DocAccessibleParent::AddChildDoc(DocAcce
   ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy;
   if (!outerDoc)
     return false;
 
   aChildDoc->mParent = outerDoc;
   outerDoc->SetChildDoc(aChildDoc);
   mChildDocs.AppendElement(aChildDoc);
   aChildDoc->mParentDoc = this;
-  ProxyCreated(aChildDoc);
+  ProxyCreated(aChildDoc, 0);
   return true;
 }
 
 PLDHashOperator
 DocAccessibleParent::ShutdownAccessibles(ProxyEntry* entry, void*)
 {
   ProxyDestroyed(entry->mProxy);
   return PL_DHASH_NEXT;
 }
 
 void
-DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy)
+DocAccessibleParent::Destroy()
 {
   MOZ_ASSERT(mChildDocs.IsEmpty(),
       "why wheren't the child docs destroyed already?");
+  MOZ_ASSERT(!mShutdown);
+  mShutdown = true;
 
   mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
   ProxyDestroyed(this);
   mParentDoc ? mParentDoc->RemoveChildDoc(this)
     : GetAccService()->RemoteDocShutdown(this);
 }
 }
 }
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -21,17 +21,17 @@ namespace a11y {
  * These objects live in the main process and comunicate with and represent
  * an accessible document in a content process.
  */
 class DocAccessibleParent : public ProxyAccessible,
     public PDocAccessibleParent
 {
 public:
   DocAccessibleParent() :
-    ProxyAccessible(this), mParentDoc(nullptr)
+    ProxyAccessible(this), mParentDoc(nullptr), mShutdown(false)
   { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
   ~DocAccessibleParent()
   {
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
     MOZ_ASSERT(!mParentDoc);
   }
 
@@ -40,17 +40,22 @@ public:
    * process it is firing an event.
    */
   virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType)
     MOZ_OVERRIDE;
 
   virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE;
   virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE;
 
-  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  void Destroy();
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
+  {
+    if (!mShutdown)
+      Destroy();
+  }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
   DocAccessibleParent* Parent() const { return mParentDoc; }
 
   /*
@@ -110,14 +115,15 @@ private:
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
+  bool mShutdown;
 };
 
 }
 }
 
 #endif
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -9,16 +9,17 @@ include protocol PContent;
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   uint32_t Role;
   uint32_t ChildrenCount;
+  uint32_t Interfaces;
 };
 
 struct ShowEventData
 {
   uint64_t ID;
   uint32_t Idx;
   AccessibleData[] NewTree;
 };
@@ -44,12 +45,14 @@ parent:
   ShowEvent(ShowEventData data);
   HideEvent(uint64_t aRootID);
 
 child:
   prio(high) sync State(uint64_t aID) returns(uint64_t states);
   prio(high) sync Name(uint64_t aID) returns(nsString name);
   prio(high) sync Description(uint64_t aID) returns(nsString desc);
   prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
+prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
+                              aEndOffset) returns(nsString aText);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -18,16 +18,21 @@ ProxyAccessible::Shutdown()
   MOZ_ASSERT(!mOuterDoc);
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
+  } else {
+    if (mChildren.Length() != 1)
+      MOZ_CRASH("outer doc doesn't own adoc!");
+
+    static_cast<DocAccessibleParent*>(mChildren[0])->Destroy();
   }
 
   mChildren.Clear();
   ProxyDestroyed(this);
   mDoc->RemoveAccessible(this);
 }
 
 void
@@ -64,10 +69,17 @@ ProxyAccessible::Description(nsString& a
   unused << mDoc->SendDescription(mID, &aDesc);
 }
 
 void
 ProxyAccessible::Attributes(nsTArray<Attribute> *aAttrs) const
 {
   unused << mDoc->SendAttributes(mID, aAttrs);
 }
+
+void
+ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
+                               nsString& aText) const
+{
+  unused << mDoc->SendTextSubstring(mID, aStartOffset, aEndOfset, &aText);
 }
 }
+}
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -75,16 +75,22 @@ public:
   void Description(nsString& aDesc) const;
 
   /**
    * Get the set of attributes on the proxied accessible.
    */
   void Attributes(nsTArray<Attribute> *aAttrs) const;
 
   /**
+   * Get the text between the given offsets.
+   */
+  void TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
+                     nsString& aText) const;
+
+  /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
    */
@@ -103,12 +109,17 @@ private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
   role mRole : 31;
   bool mOuterDoc : 1;
 };
 
+enum Interfaces
+{
+  HYPERTEXT = 1
+};
+
 }
 }
 
 #endif
--- a/accessible/mac/Platform.mm
+++ b/accessible/mac/Platform.mm
@@ -29,17 +29,17 @@ PlatformInit()
 }
 
 void
 PlatformShutdown()
 {
 }
 
 void
-ProxyCreated(ProxyAccessible*)
+ProxyCreated(ProxyAccessible*, uint32_t)
 {
 }
 
 void
 ProxyDestroyed(ProxyAccessible*)
 {
 }
 
--- a/accessible/other/Platform.cpp
+++ b/accessible/other/Platform.cpp
@@ -15,17 +15,17 @@ a11y::PlatformInit()
 }
 
 void
 a11y::PlatformShutdown()
 {
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible*)
+a11y::ProxyCreated(ProxyAccessible*, uint32_t)
 {
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible*)
 {
 }
 
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -30,17 +30,17 @@ void
 a11y::PlatformShutdown()
 {
   ::DestroyCaret();
 
   nsWinUtils::ShutdownWindowEmulation();
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible*)
+a11y::ProxyCreated(ProxyAccessible*, uint32_t)
 {
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible*)
 {
 }
 
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -22,17 +22,19 @@
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #define XRE_DONT_SUPPORT_XPSP2 // See https://bugzil.la/1023941#c32
 #include "nsWindowsWMain.cpp"
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
 #define snprintf _snprintf
+#endif
 #define strcasecmp _stricmp
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "GonkDisplay.h"
 #endif
 
 #include "BinaryPath.h"
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -363,113 +363,69 @@ var shell = {
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
     IndexedDBPromptHelper.uninit();
   },
 
-  // If this key event actually represents a hardware button, send a
-  // mozChromeEvent with detail.type set to 'xxx-button-press' or
-  // 'xxx-button-release' instead. Note that no more mozChromeEvent for hardware
-  // buttons needed after Bug 1014418 is landed.
-  filterHardwareKeys: function shell_filterHardwareKeys(evt) {
-    var type;
-    switch (evt.keyCode) {
-      case evt.DOM_VK_HOME:         // Home button
-        type = 'home-button';
-        break;
-      case evt.DOM_VK_SLEEP:        // Sleep button
-      case evt.DOM_VK_END:          // On desktop we don't have a sleep button
-        type = 'sleep-button';
-        break;
-      case evt.DOM_VK_VOLUME_UP:      // Volume up button
-        type = 'volume-up-button';
-        break;
-      case evt.DOM_VK_VOLUME_DOWN:    // Volume down button
-        type = 'volume-down-button';
-        break;
-      case evt.DOM_VK_ESCAPE:       // Back button (should be disabled)
-        type = 'back-button';
-        break;
-      case evt.DOM_VK_CONTEXT_MENU: // Menu button
-        type = 'menu-button';
-        break;
-      case evt.DOM_VK_F1: // headset button
-        type = 'headset-button';
-        break;
-    }
+  // If this key event represents a hardware button which needs to be send as
+  // a message, broadcasts it with the message set to 'xxx-button-press' or
+  // 'xxx-button-release'.
+  broadcastHardwareKeys: function shell_broadcastHardwareKeys(evt) {
+    let type;
+    let message;
 
     let mediaKeys = {
       'MediaTrackNext': 'media-next-track-button',
       'MediaTrackPrevious': 'media-previous-track-button',
       'MediaPause': 'media-pause-button',
       'MediaPlay': 'media-play-button',
       'MediaPlayPause': 'media-play-pause-button',
       'MediaStop': 'media-stop-button',
       'MediaRewind': 'media-rewind-button',
       'MediaFastForward': 'media-fast-forward-button'
     };
 
-    let isMediaKey = false;
-    if (mediaKeys[evt.key]) {
-      isMediaKey = true;
-      type = mediaKeys[evt.key];
-    }
-
-    // The key doesn't represent a hardware button, so no mozChromeEvent.
-    if (!type) {
+    if (evt.keyCode == evt.DOM_VK_F1) {
+      type = 'headset-button';
+      message = 'headset-button';
+    } else if (mediaKeys[evt.key]) {
+      type = 'media-button';
+      message = mediaKeys[evt.key];
+    } else {
       return;
     }
 
     switch (evt.type) {
       case 'keydown':
-        type = type + '-press';
+        message = message + '-press';
         break;
       case 'keyup':
-        type = type + '-release';
+        message = message + '-release';
         break;
     }
 
-    // Let applications receive the headset button key press/release event.
-    if (evt.keyCode == evt.DOM_VK_F1 && type !== this.lastHardwareButtonEventType) {
-      this.lastHardwareButtonEventType = type;
-      gSystemMessenger.broadcastMessage('headset-button', type);
-      return;
-    }
-
-    if (isMediaKey) {
-      this.lastHardwareButtonEventType = type;
-      gSystemMessenger.broadcastMessage('media-button', type);
-      return;
-    }
-
-    // On my device, the physical hardware buttons (sleep and volume)
-    // send multiple events (press press release release), but the
-    // soft home button just sends one.  This hack is to manually
-    // "debounce" the keys. If the type of this event is the same as
-    // the type of the last one, then don't send it.  We'll never send
-    // two presses or two releases in a row.
-    // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=761067
-    if (type !== this.lastHardwareButtonEventType) {
-      this.lastHardwareButtonEventType = type;
-      this.sendChromeEvent({type: type});
+    // Let applications receive the headset button and media key press/release message.
+    if (message !== this.lastHardwareButtonMessage) {
+      this.lastHardwareButtonMessage = message;
+      gSystemMessenger.broadcastMessage(type, message);
     }
   },
 
-  lastHardwareButtonEventType: null, // property for the hack above
+  lastHardwareButtonMessage: null, // property for the hack above
   visibleNormalAudioActive: false,
 
   handleEvent: function shell_handleEvent(evt) {
     let content = this.contentBrowser.contentWindow;
     switch (evt.type) {
       case 'keydown':
       case 'keyup':
-        this.filterHardwareKeys(evt);
+        this.broadcastHardwareKeys(evt);
         break;
       case 'mozfullscreenchange':
         // When the screen goes fullscreen make sure to set the focus to the
         // main window so noboby can prevent the ESC key to get out fullscreen
         // mode
         if (document.mozFullScreen)
           Services.fm.focusedWindow = window;
         break;
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
@@ -134,12 +134,12 @@
   <project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="6c6f012cea17fb8b3263605737816cf6663432f1"/>
   <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
   <project name="u-boot" path="u-boot" revision="5167e5eec5cb6b3147839da158637e6d953a4e4f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
-  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="a6f913f0e114945d995680a6fe5cccb24c02b3c2"/>
+  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="f56ab768cb9f1ad42fb0809ffec1424b1e693369"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "5e3f9375165bca64a0066f51fbd79bd32e380a6b", 
+    "revision": "03effd58034893d2a12907faa6a8a41d3e923b50", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -38,17 +38,19 @@
 #ifdef XP_WIN
 // we want a wmain entry point
 #ifdef MOZ_ASAN
 // ASAN requires firefox.exe to be built with -MD, and it's OK if we don't
 // support Windows XP SP2 in ASAN builds.
 #define XRE_DONT_SUPPORT_XPSP2
 #endif
 #include "nsWindowsWMain.cpp"
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
 #define snprintf _snprintf
+#endif
 #define strcasecmp _stricmp
 #endif
 #include "BinaryPath.h"
 
 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
 
 #include "mozilla/Telemetry.h"
 #include "mozilla/WindowsDllBlocklist.h"
--- a/browser/config/mozconfigs/linux32/common-opt
+++ b/browser/config/mozconfigs/linux32/common-opt
@@ -1,14 +1,15 @@
 # This file is sourced by nightly, beta, and release mozconfigs.
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-google-api-keyfile=/builds/gapi.data
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
+ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -1,15 +1,13 @@
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 ac_add_options --enable-signmar
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
 
-ac_add_options --disable-unified-compilation
-
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 #Use ccache
--- a/browser/config/mozconfigs/linux64/common-opt
+++ b/browser/config/mozconfigs/linux64/common-opt
@@ -1,14 +1,15 @@
 # This file is sourced by the nightly, beta, and release mozconfigs.
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-google-api-keyfile=/builds/gapi.data
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
+ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
deleted file mode 100644
--- a/browser/config/mozconfigs/linux64/debug-nonunified
+++ /dev/null
@@ -1,5 +0,0 @@
-MOZ_AUTOMATION_UPLOAD=0
-
-. "$topsrcdir/browser/config/mozconfigs/linux64/debug"
-
-ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang
@@ -2,18 +2,16 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
-ac_add_options --disable-unified-compilation
-
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
 ac_add_options --enable-clang-plugin
 
 # Avoid dependency on libstdc++ 4.7
deleted file mode 100644
--- a/browser/config/mozconfigs/linux64/nightly-nonunified
+++ /dev/null
@@ -1,6 +0,0 @@
-MOZ_AUTOMATION_UPLOAD=0
-MOZ_AUTOMATION_PRETTY=1
-
-. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
-
-ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/macosx-universal/common-opt
+++ b/browser/config/mozconfigs/macosx-universal/common-opt
@@ -4,16 +4,17 @@
 
 # Universal builds override the default of browser (bug 575283 comment 29)
 ac_add_options --enable-application=browser
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-google-api-keyfile=/builds/gapi.data
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
+ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
deleted file mode 100644
--- a/browser/config/mozconfigs/macosx-universal/nightly-nonunified
+++ /dev/null
@@ -1,5 +0,0 @@
-MOZ_AUTOMATION_PRETTY=1
-MOZ_AUTOMATION_UPLOAD=0
-. "$topsrcdir/browser/config/mozconfigs/macosx-universal/nightly"
-
-ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/macosx64/debug-asan
+++ b/browser/config/mozconfigs/macosx64/debug-asan
@@ -1,17 +1,15 @@
 . $topsrcdir/build/unix/mozconfig.asan
 
 ac_add_options --enable-application=browser
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
 
-ac_add_options --disable-unified-compilation
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
 # Need this to prevent name conflicts with the normal nightly build packages
deleted file mode 100644
--- a/browser/config/mozconfigs/macosx64/debug-nonunified
+++ /dev/null
@@ -1,4 +0,0 @@
-MOZ_AUTOMATION_UPLOAD=0
-. "$topsrcdir/browser/config/mozconfigs/macosx64/debug"
-
-ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/win32/common-opt
+++ b/browser/config/mozconfigs/win32/common-opt
@@ -15,16 +15,17 @@ fi
 ac_add_options --with-google-api-keyfile=${_gapi_keyfile}
 
 if [ -f /c/builds/google-oauth-api.key ]; then
   _google_oauth_api_keyfile=/c/builds/google-oauth-api.key
 else
   _google_oauth_api_keyfile=/e/builds/google-oauth-api.key
 fi
 ac_add_options --with-google-oauth-api-keyfile=${_google_oauth_api_keyfile}
+ac_add_options --with-mozilla-api-keyfile=/c/builds/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
deleted file mode 100644
--- a/browser/config/mozconfigs/win32/debug-nonunified
+++ /dev/null
@@ -1,6 +0,0 @@
-. "$topsrcdir/build/mozconfig.win-common"
-MOZ_AUTOMATION_L10N_CHECK=0
-MOZ_AUTOMATION_UPLOAD=0
-. "$topsrcdir/browser/config/mozconfigs/win32/debug"
-
-ac_add_options --disable-unified-compilation
deleted file mode 100644
--- a/browser/config/mozconfigs/win32/nightly-nonunified
+++ /dev/null
@@ -1,6 +0,0 @@
-. "$topsrcdir/build/mozconfig.win-common"
-MOZ_AUTOMATION_PRETTY=1
-MOZ_AUTOMATION_UPLOAD=0
-. "$topsrcdir/browser/config/mozconfigs/win32/nightly"
-
-ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/win64/common-opt
+++ b/browser/config/mozconfigs/win64/common-opt
@@ -13,16 +13,17 @@ fi
 ac_add_options --with-google-api-keyfile=${_gapi_keyfile}
 
 if [ -f /c/builds/google-oauth-api.key ]; then
   _google_oauth_api_keyfile=/c/builds/google-oauth-api.key
 else
   _google_oauth_api_keyfile=/e/builds/google-oauth-api.key
 fi
 ac_add_options --with-google-oauth-api-keyfile=${_google_oauth_api_keyfile}
+ac_add_options --with-mozilla-api-keyfile=/c/builds/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
deleted file mode 100644
--- a/browser/config/mozconfigs/win64/debug-nonunified
+++ /dev/null
@@ -1,3 +0,0 @@
-. "$topsrcdir/browser/config/mozconfigs/win64/debug"
-
-ac_add_options --disable-unified-compilation
deleted file mode 100644
--- a/browser/config/mozconfigs/win64/nightly-nonunified
+++ /dev/null
@@ -1,3 +0,0 @@
-. "$topsrcdir/browser/config/mozconfigs/win64/nightly"
-
-ac_add_options --disable-unified-compilation
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -60,16 +60,18 @@ http://mochi.test:8888   primary,privile
 # These are a common set of prefixes scattered across one TLD with two ports and
 # another TLD on a single port.
 #
 http://127.0.0.1:80               privileged
 http://127.0.0.1:8888             privileged
 http://test:80                    privileged
 http://mochi.test:8888            privileged
 http://test1.mochi.test:8888
+http://sub1.test1.mochi.test:8888
+http://sub2.xn--lt-uia.mochi.test:8888
 http://test2.mochi.test:8888
 http://example.org:80                privileged
 http://test1.example.org:80          privileged
 http://test2.example.org:80          privileged
 http://sub1.test1.example.org:80     privileged
 http://sub1.test2.example.org:80     privileged
 http://sub2.test1.example.org:80     privileged
 http://sub2.test2.example.org:80     privileged
--- a/configure.in
+++ b/configure.in
@@ -3538,27 +3538,16 @@ case "$GRE_MILESTONE" in
       RELEASE_BUILD=1
       AC_DEFINE(RELEASE_BUILD)
       ;;
 esac
 AC_SUBST(NIGHTLY_BUILD)
 AC_SUBST(RELEASE_BUILD)
 
 dnl ========================================================
-dnl Disable compiling sources in unified mode.
-dnl ========================================================
-
-MOZ_ARG_DISABLE_BOOL(unified-compilation,
-[  --disable-unified-compilation
-                          Disable unified compilation of some C/C++ sources],
-    MOZ_DISABLE_UNIFIED_COMPILATION=1,
-    MOZ_DISABLE_UNIFIED_COMPILATION=)
-AC_SUBST(MOZ_DISABLE_UNIFIED_COMPILATION)
-
-dnl ========================================================
 dnl Multiprocess Firefox Nightly Testing UI
 dnl To be removed in Bug 1003313
 dnl ========================================================
 if test -n "$NIGHTLY_BUILD"; then
     E10S_TESTING_ONLY=1
     AC_DEFINE(E10S_TESTING_ONLY)
 fi
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5799,33 +5799,31 @@ nsDocShell::SetPosition(int32_t x, int32
         NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetPosition(int32_t * aX, int32_t * aY)
 {
-    int32_t dummyHolder;
-    return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
+    return GetPositionAndSize(aX, aY, nullptr, nullptr);
 }
 
 NS_IMETHODIMP
 nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
 {
     int32_t x = 0, y = 0;
     GetPosition(&x, &y);
     return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
 }
 
 NS_IMETHODIMP
 nsDocShell::GetSize(int32_t * aCX, int32_t * aCY)
 {
-    int32_t dummyHolder;
-    return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
+    return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
 }
 
 NS_IMETHODIMP
 nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx,
                                int32_t cy, bool fRepaint)
 {
     mBounds.x = x;
     mBounds.y = y;
@@ -9181,17 +9179,19 @@ nsDocShell::CheckLoadingPermissions()
         // Permit all loads.
 
         return rv;
     }
 
     // Note - The check for a current JSContext here isn't necessarily sensical.
     // It's just designed to preserve the old semantics during a mass-conversion
     // patch.
-    NS_ENSURE_TRUE(nsContentUtils::GetCurrentJSContext(), NS_OK);
+    if (!nsContentUtils::GetCurrentJSContext()) {
+      return NS_OK;
+    }
 
     // Check if the caller is from the same origin as this docshell,
     // or any of its ancestors.
     nsCOMPtr<nsIDocShellTreeItem> item(this);
     do {
         nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
         nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
 
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -208,23 +208,22 @@ NS_GetContentList(nsINode* aRootNode,
 
   static const PLDHashTableOps hash_table_ops =
   {
     PL_DHashAllocTable,
     PL_DHashFreeTable,
     ContentListHashtableHashKey,
     ContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
-    PL_DHashClearEntryStub,
-    PL_DHashFinalizeStub
+    PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gContentListHashTable.ops) {
-    PL_DHashTableInit(&gContentListHashTable, &hash_table_ops, nullptr,
+    PL_DHashTableInit(&gContentListHashTable, &hash_table_ops,
                       sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gContentListHashTable.ops) {
 
     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
@@ -321,24 +320,23 @@ GetFuncStringContentList(nsINode* aRootN
 
   static const PLDHashTableOps hash_table_ops =
   {
     PL_DHashAllocTable,
     PL_DHashFreeTable,
     FuncStringContentListHashtableHashKey,
     FuncStringContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
-    PL_DHashClearEntryStub,
-    PL_DHashFinalizeStub
+    PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gFuncStringContentListHashTable.ops) {
     PL_DHashTableInit(&gFuncStringContentListHashTable, &hash_table_ops,
-                      nullptr, sizeof(FuncStringContentListHashEntry));
+                      sizeof(FuncStringContentListHashEntry));
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable.ops) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -480,22 +480,21 @@ nsContentUtils::Init()
     static const PLDHashTableOps hash_table_ops =
     {
       PL_DHashAllocTable,
       PL_DHashFreeTable,
       PL_DHashVoidPtrKeyStub,
       PL_DHashMatchEntryStub,
       PL_DHashMoveEntryStub,
       EventListenerManagerHashClearEntry,
-      PL_DHashFinalizeStub,
       EventListenerManagerHashInitEntry
     };
 
     PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
-                      nullptr, sizeof(EventListenerManagerMapEntry));
+                      sizeof(EventListenerManagerMapEntry));
 
     RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
   }
 
   sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
@@ -6254,25 +6253,41 @@ void nsContentUtils::RemoveNewlines(nsSt
   // strip CR/LF and null
   static const char badChars[] = {'\r', '\n', 0};
   aString.StripChars(badChars);
 }
 
 void
 nsContentUtils::PlatformToDOMLineBreaks(nsString &aString)
 {
+  if (!PlatformToDOMLineBreaks(aString, mozilla::fallible_t())) {
+    aString.AllocFailed(aString.Length());
+  }
+}
+
+bool
+nsContentUtils::PlatformToDOMLineBreaks(nsString& aString, const fallible_t& aFallible)
+{
   if (aString.FindChar(char16_t('\r')) != -1) {
     // Windows linebreaks: Map CRLF to LF:
-    aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                             MOZ_UTF16("\n"));
+    if (!aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
+                                  MOZ_UTF16("\n"),
+                                  aFallible)) {
+      return false;
+    }
 
     // Mac linebreaks: Map any remaining CR to LF:
-    aString.ReplaceSubstring(MOZ_UTF16("\r"),
-                             MOZ_UTF16("\n"));
-  }
+    if (!aString.ReplaceSubstring(MOZ_UTF16("\r"),
+                                  MOZ_UTF16("\n"),
+                                  aFallible)) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 void
 nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
                                                nsAString& aResultString)
 {
   MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1710,16 +1710,18 @@ public:
    */
   static void RemoveNewlines(nsString &aString);
 
   /**
    * Convert Windows and Mac platform linebreaks to \n.
    * @param aString the string to convert the newlines inside [in/out]
    */
   static void PlatformToDOMLineBreaks(nsString &aString);
+  static NS_WARN_UNUSED_RESULT bool PlatformToDOMLineBreaks(nsString &aString,
+                                                            const mozilla::fallible_t&);
 
   /**
    * Populates aResultString with the contents of the string-buffer aBuf, up
    * to aBuf's null-terminator.  aBuf must not be null. Ownership of the string
    * is not transferred.
    */
   static void PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
                                              nsAString& aResultString);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3973,22 +3973,20 @@ nsDocument::SetSubDocumentFor(Element* a
       static const PLDHashTableOps hash_table_ops =
       {
         PL_DHashAllocTable,
         PL_DHashFreeTable,
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         SubDocClearEntry,
-        PL_DHashFinalizeStub,
         SubDocInitEntry
       };
 
-      mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr,
-                                       sizeof(SubDocMapEntry));
+      mSubDocuments = PL_NewDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
       if (!mSubDocuments) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
     }
 
     // Add a mapping to the hash table
     SubDocMapEntry *entry =
       static_cast<SubDocMapEntry*>
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1958,46 +1958,45 @@ nsFrameLoader::UpdatePositionAndSize(nsS
       nsIntSize size = aIFrame->GetSubdocumentSize();
       nsIntRect dimensions;
       NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
       nsIntPoint chromeDisp = aIFrame->GetChromeDisplacement();
       mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
     }
     return NS_OK;
   }
-  return UpdateBaseWindowPositionAndSize(aIFrame);
+  UpdateBaseWindowPositionAndSize(aIFrame);
+  return NS_OK;
 }
 
-nsresult
+void
 nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame)
 {
   nsCOMPtr<nsIDocShell> docShell;
   GetDocShell(getter_AddRefs(docShell));
   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
 
   // resize the sub document
   if (baseWindow) {
     int32_t x = 0;
     int32_t y = 0;
 
     nsWeakFrame weakFrame(aIFrame);
 
-    baseWindow->GetPositionAndSize(&x, &y, nullptr, nullptr);
+    baseWindow->GetPosition(&x, &y);
 
     if (!weakFrame.IsAlive()) {
-      // GetPositionAndSize() killed us
-      return NS_OK;
+      // GetPosition() killed us
+      return;
     }
 
     nsIntSize size = aIFrame->GetSubdocumentSize();
 
     baseWindow->SetPositionAndSize(x, y, size.width, size.height, false);
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetEventMode(uint32_t* aEventMode)
 {
   *aEventMode = mEventMode;
   return NS_OK;
 }
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -282,17 +282,17 @@ private:
   nsresult MaybeCreateDocShell();
   nsresult EnsureMessageManager();
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
   // Updates the subdocument position and size. This gets called only
   // when we have our own in-process DocShell.
-  nsresult UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);
+  void UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);
   nsresult CheckURILoad(nsIURI* aURI);
   void FireErrorEvent();
   nsresult ReallyStartLoadingInternal();
 
   // Return true if remote browser created; nothing else to do
   bool TryRemoteBrowser();
 
   // Tell the remote browser that it's now "virtually visible"
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -275,57 +275,56 @@ nsPropertyTable::GetPropertyListFor(nsIA
       break;
     }
   }
 
   return result;
 }
 
 //----------------------------------------------------------------------
-    
+
 nsPropertyTable::PropertyList::PropertyList(nsIAtom            *aName,
                                             NSPropertyDtorFunc  aDtorFunc,
                                             void               *aDtorData,
                                             bool                aTransfer)
   : mName(aName),
     mDtorFunc(aDtorFunc),
     mDtorData(aDtorData),
     mTransfer(aTransfer),
     mNext(nullptr)
 {
-  PL_DHashTableInit(&mObjectValueMap, PL_DHashGetStubOps(), this,
+  PL_DHashTableInit(&mObjectValueMap, PL_DHashGetStubOps(),
                     sizeof(PropertyListMapEntry));
 }
 
 nsPropertyTable::PropertyList::~PropertyList()
 {
   PL_DHashTableFinish(&mObjectValueMap);
 }
 
 
 static PLDHashOperator
 DestroyPropertyEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
                           uint32_t number, void *arg)
 {
   nsPropertyTable::PropertyList *propList =
-      static_cast<nsPropertyTable::PropertyList*>(table->data);
+      static_cast<nsPropertyTable::PropertyList*>(arg);
   PropertyListMapEntry* entry = static_cast<PropertyListMapEntry*>(hdr);
 
   propList->mDtorFunc(const_cast<void*>(entry->key), propList->mName,
                       entry->value, propList->mDtorData);
   return PL_DHASH_NEXT;
 }
 
 void
 nsPropertyTable::PropertyList::Destroy()
 {
   // Enumerate any remaining object/value pairs and destroy the value object
   if (mDtorFunc)
-    PL_DHashTableEnumerate(&mObjectValueMap, DestroyPropertyEnumerator,
-                           nullptr);
+    PL_DHashTableEnumerate(&mObjectValueMap, DestroyPropertyEnumerator, this);
 }
 
 bool
 nsPropertyTable::PropertyList::DeletePropertyFor(nsPropertyOwner aObject)
 {
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
                                            (PL_DHashTableLookup(&mObjectValueMap, aObject));
   if (!PL_DHASH_ENTRY_IS_BUSY(entry))
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -321,28 +321,27 @@ nsScriptNameSpaceManager::Init()
   static const PLDHashTableOps hash_table_ops =
   {
     PL_DHashAllocTable,
     PL_DHashFreeTable,
     GlobalNameHashHashKey,
     GlobalNameHashMatchEntry,
     PL_DHashMoveEntryStub,
     GlobalNameHashClearEntry,
-    PL_DHashFinalizeStub,
     GlobalNameHashInitEntry
   };
 
   mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
-                                     nullptr, sizeof(GlobalNameMapEntry),
+                                     sizeof(GlobalNameMapEntry),
                                      fallible_t(),
                                      GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
 
   mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
-                                     nullptr, sizeof(GlobalNameMapEntry),
+                                     sizeof(GlobalNameMapEntry),
                                      fallible_t(),
                                      GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
   if (!mIsInitialized) {
     PL_DHashTableFinish(&mGlobalNames);
 
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
deleted file mode 100644
index e64729dedf2bb2f539fcc572541fbf4f7229d394..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index 0000000000000000000000000000000000000000..5c6f48a40a279334ba99dd7cd65ec1ed134e456f
GIT binary patch
literal 439
zc%17D@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)OEIV65|WaSW-r_4dj^P6h)W=8a3b
z+jH)T{?T!jIw<pQm1a<O(*;AuLrxJK+%2se1jIPF6BTs?#DD?{IzRyjpm<^<Q2Y=R
zP`rhejta5p+_UyL+eXuA$Eo88MIB*U`*Ht{zf)NFW`!HR0Y(Uer>mdKI;Vst05Q&k
AP5=M^
new file mode 100644
index 0000000000000000000000000000000000000000..727fe15a8cb5e73072a4cbb42b0e6f9a917c1050
GIT binary patch
literal 444
zc%17D@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)OEIU~KkuaSW-r_4dj^P6h=Y7Dv(F
zUfC~wF3r?^qG=R-XyctTHqGmd94{L&9&(D{;BIN%ARxxUov5fIAO;jr&;bfK0L2p<
zf#QdlfZ{EzbX15%=bqf-8rmkIHx4Az%nIuI;;6cwxjUQC_wJrdV5~5By85}Sb4q9e
E0N1R8$N&HU
new file mode 100644
index 0000000000000000000000000000000000000000..98d5e0feca3bc82f0bbd72dc055ef347c66a385a
GIT binary patch
literal 441
zc%17D@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)OEIU~KSoaSW-r_4dj^P6h=Y7Dv(F
zenC68O;~=yhx2%m@n@c0r&xSXvv6=HD(VP`9de3L&}nJi;1B^6Xxso4U;>J_umZ(7
zxPjsVVsuo9Md!zDvWwST2uWn_r<oO0_Qm`iP3>$#>pv*y0;7b%)78&qol`;+0PC)W
A-v9sr
deleted file mode 100644
index 02764182e5850fbc2648d8d03fcc19f003c83456..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5eedbdfb1c5227d31b6039752f3d21ace0004b62..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 838b9566119961ac8cb51095733428734d1e93fb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -1,189 +1,149 @@
 # WebGL Reftests!
 default-preferences pref(webgl.force-enabled,true)
 
 # Check that disabling works:
-                           == webgl-disable-test.html?nogl  wrapper.html?green.png
-pref(webgl.disabled,true)  == webgl-disable-test.html       wrapper.html?green.png
+                           == webgl-disable-test.html?nogl wrapper.html?green.png
+pref(webgl.disabled,true)  == webgl-disable-test.html      wrapper.html?green.png
 
 # Basic WebGL tests:
 # Do we get pixels to the screen at all?
-# Try to just hit the different rendering paths here.
-
-# Android 2.2 ARMv6 slaves can't seem to use WebGL, but we can't seem to
-# disable the ARMv6 runs without disabling ARMv7, which works fine.
-# For now, just mark versions <15 (<4.0) as random, so we still get
-# assert coverage.
+# Neither of these should ever break.
+                                        == webgl-clear-test.html wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-clear-test.html?readback wrapper.html?green.png
 
-# Test: {aa, alpha, preserve, readback} = 16
-                     random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?__&_____&________  wrapper.html?green.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?aa&_____&________  wrapper.html?green.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?__&alpha&________  wrapper.html?green.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?aa&alpha&________  wrapper.html?green.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?__&_____&preserve  wrapper.html?green.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?aa&_____&preserve  wrapper.html?green.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?__&alpha&preserve  wrapper.html?green.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?aa&alpha&preserve  wrapper.html?green.png
-
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&__&_____&________  wrapper.html?green.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&aa&_____&________  wrapper.html?green.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&__&alpha&________  wrapper.html?green.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&aa&alpha&________  wrapper.html?green.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&__&_____&preserve  wrapper.html?green.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&aa&_____&preserve  wrapper.html?green.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&__&alpha&preserve  wrapper.html?green.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,91) random-if(Android&&AndroidVersion<15)  == webgl-clear-test.html?readback&aa&alpha&preserve  wrapper.html?green.png
+# Make sure that our choice of attribs doesn't break rendering.
+== webgl-clear-test.html?depth wrapper.html?green.png
+== webgl-clear-test.html?stencil wrapper.html?green.png
+== webgl-clear-test.html?depth&stencil wrapper.html?green.png
 
 # Check that resize works:
-random-if(Android&&AndroidVersion<15)  == webgl-resize-test.html  wrapper.html?green.png
+== webgl-resize-test.html  wrapper.html?green.png
 
-# Check orientation:
-                     random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?__&_____&________  wrapper.html?white-top-left.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?aa&_____&________  wrapper.html?white-top-left.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?__&alpha&________  wrapper.html?white-top-left.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?aa&alpha&________  wrapper.html?white-top-left.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?__&_____&preserve  wrapper.html?white-top-left.png
-                     random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?aa&_____&preserve  wrapper.html?white-top-left.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?__&alpha&preserve  wrapper.html?white-top-left.png
-random-if(gtk2Widget) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?aa&alpha&preserve  wrapper.html?white-top-left.png
+# Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on
+# failures encountered when running on Try, and then targetting the Try config by
+# differences in the `sandbox` contents. That is, I'm labeling based on symptoms rather
+# than cause.
+# Lin-R-e10s: gtk2Widget && browserIsRemote
+# WinXP-R: winWidget && layersGPUAccelerated && !d2d
+# Win7+-R: winWidget && layersGPUAccelerated && d2d
+# Win7+-Ru: winWidget && !layersGPUAccelerated
+# (Note that we have to remove spaces when used below)
 
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&__&_____&________  wrapper.html?white-top-left.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&aa&_____&________  wrapper.html?white-top-left.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&__&alpha&________  wrapper.html?white-top-left.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&aa&alpha&________  wrapper.html?white-top-left.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&__&_____&preserve  wrapper.html?white-top-left.png
-pref(webgl.force-layers-readback,true)                      random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&aa&_____&preserve  wrapper.html?white-top-left.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&__&alpha&preserve  wrapper.html?white-top-left.png
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) fuzzy-if(B2G,256,90) random-if(Android&&AndroidVersion<15)  == webgl-orientation-test.html?readback&aa&alpha&preserve  wrapper.html?white-top-left.png
+# IMPORTANT: Expected outcomes are evaluated left-to-right, and they replace eachother.
+# That means that if an unconditional status (`fuzzy()`) is to the right of another status
+# (such as fails-if), it will overwrite the old status.
+#
+# As such, all unconditional statuses should be to the left of conditional statuses.
+# (See /layout/tools/reftest/reftest.js:945)
 
-# Does we draw the correct color in the correct places with all context creation options?
-# (Note that our context creation option matrix is 2^6 = 64)
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&_______&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&premult&________&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&premult&________&_______  wrapper.html?colors.png # Bug 844439
-
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&_______&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&premult&preserve&_______  wrapper.html?colors.png # Bug 844439
+# Does we draw the correct colors in the correct places?
+# Combinations: PowerSet([readback, aa, preserve, premult, alpha]) x [frame=1,frame=6]
+# This is 2^6 = 64 combinations.
+                                                                                                         == webgl-color-test.html?frame=1&__&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&__&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&__&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&__&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=1&__&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=1&aa&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=1&__&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=1&aa&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+                                                                                                         == webgl-color-test.html?frame=1&__&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=1&__&preserve&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=1&aa&preserve&premult&alpha  wrapper.html?colors-premult.png
 
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&_______&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&premult&________&stencil  wrapper.html?colors.png # Bug 844439
-
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&_______&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&_____&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&_____&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&_____&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&_____&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&_____&depth&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&_____&depth&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?__&alpha&depth&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?aa&alpha&depth&premult&preserve&stencil  wrapper.html?colors.png # Bug 844439
-
+                                                                                                         == webgl-color-test.html?frame=6&__&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=6&aa&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=6&__&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+fails-if(winWidget&&layersGPUAccelerated&&d2d)                                                           == webgl-color-test.html?frame=6&aa&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=6&__&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=6&aa&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                         == webgl-color-test.html?frame=6&__&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+fails-if(winWidget&&layersGPUAccelerated&&d2d)                                                           == webgl-color-test.html?frame=6&aa&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=6&__&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=6&aa&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)     == webgl-color-test.html?frame=6&__&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&(!layersGPUAccelerated||!d2d))  == webgl-color-test.html?frame=6&aa&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+                                                                                                         == webgl-color-test.html?frame=6&__&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=6&aa&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=6&__&preserve&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                         == webgl-color-test.html?frame=6&aa&preserve&premult&alpha  wrapper.html?colors-premult.png
 
-# Check a smaller selection for readback:
-pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&__&_____&________  wrapper.html?colors.png # Bug 844439
-pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&aa&_____&________  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&__&alpha&________  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&aa&alpha&________  wrapper.html?colors.png # Bug 844439
-pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&__&_____&preserve  wrapper.html?colors.png # Bug 844439
-pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&aa&_____&preserve  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&__&alpha&preserve  wrapper.html?colors.png # Bug 844439
-random-if(gtk2Widget) pref(webgl.force-layers-readback,true) random-if(B2G) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?readback&aa&alpha&preserve  wrapper.html?colors.png # Bug 844439
-
-
-# Check alpha behavior:
-fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=1.0  wrapper.html?colors.png
-# These tests don't use wrapper.html, as there appear to be invalidation issues with black.png and async image decoding - Bug 803299
-random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.0&alphaVal=1.0  black.html
-
-fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0        wrapper.html?colors.png
-random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0&alpha  wrapper.html?white.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&__&preserve&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=1&readback&aa&preserve&premult&alpha  wrapper.html?colors-premult.png
 
-fuzzy(1,65536) fuzzy-if(B2G,256,83) fuzzy-if(Android||B2G,9,65536) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=1.0  wrapper.html?half-colors.png
-
-# Test premult:
-# random-if(B2G) from bug 983650
-# Mark as failing on WinXP (winWidget&&!d2d), because Try said so. WFM outside of Try.
-# This might work on WinXP again if OMTC is disabled.
-fuzzy(1,65536) random-if(gtk2Widget) random-if(winWidget&&!d2d) random-if(B2G) fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.5&alpha          wrapper.html?colors-half-alpha.png
-fuzzy(1,65536) random-if(gtk2Widget) fails-if(winWidget&&!d2d)                 fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha          wrapper.html?half-colors-half-alpha.png
-# random-if(B2G) from bug 983650
-fuzzy(1,65536)                       random-if(B2G) fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha&premult  wrapper.html?colors-half-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&________&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+fails-if(winWidget&&layersGPUAccelerated&&d2d)                                                          pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&________&premult&_____  wrapper.html?colors-no-alpha.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+random-if(winWidget&&layersGPUAccelerated&&d2d)                                                         pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d2d)    pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(1,30000) fails-if(gtk2Widget&&browserIsRemote) fails-if(winWidget&&(!layersGPUAccelerated||!d2d)) pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&preserve&_______&alpha  wrapper.html?colors-non-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&________&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&__&preserve&premult&alpha  wrapper.html?colors-premult.png
+                                                                                                        pref(webgl.force-layers-readback,true)  == webgl-color-test.html?frame=6&readback&aa&preserve&premult&alpha  wrapper.html?colors-premult.png
 
-# Check for hanging framebuffer bindings:
-                                       random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?__&________  wrapper.html?green.png
-                                       random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?aa&________  wrapper.html?green.png
-pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?__&readback  wrapper.html?green.png
-pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?aa&readback  wrapper.html?green.png
+# Check for hanging bindings/state settings:
+== webgl-hanging-fb-test.html?__&________  wrapper.html?green.png
+== webgl-hanging-fb-test.html?aa&________  wrapper.html?green.png
+== webgl-hanging-fb-test.html?__&preserve  wrapper.html?green.png
+== webgl-hanging-fb-test.html?aa&preserve  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&__&________  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&aa&________  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&__&preserve  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&aa&preserve  wrapper.html?green.png
 
-                                       random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?__&________  wrapper.html?green.png
-                                       random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?aa&________  wrapper.html?green.png
-pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?__&readback  wrapper.html?green.png
-pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?aa&readback  wrapper.html?green.png
+== webgl-hanging-scissor-test.html?__  wrapper.html?green.png
+== webgl-hanging-scissor-test.html?aa  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-scissor-test.html?readback&__  wrapper.html?green.png
+pref(webgl.force-layers-readback,true)  == webgl-hanging-scissor-test.html?readback&aa  wrapper.html?green.png
 
 
 # Check that our experimental prefs still work:
 
-# 16bpp:
-skip-if(winWidget) pref(webgl.prefer-16bpp,true)                                        random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?16bpp           wrapper.html?colors.png
-skip-if(winWidget) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?16bpp&readback  wrapper.html?colors.png
+# 16bpp for Android/B2G: [16bpp] * PowerSet([readback, premult, alpha])
+# RGB565 dithers 127 to [123,132]. (Max error: 5)
+# RGBA4444 dithers 128 to [119,136], and 191 to [192]. (Max error: 9)
+fuzzy(5,30000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true)                                         == webgl-color-test.html?16bpp&________&_______&_____  wrapper.html?colors-no-alpha.png
+fuzzy(5,30000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true)  == webgl-color-test.html?16bpp&readback&_______&_____  wrapper.html?colors-no-alpha.png
+fuzzy(5,30000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true)                                         == webgl-color-test.html?16bpp&________&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(5,30000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true)  == webgl-color-test.html?16bpp&readback&premult&_____  wrapper.html?colors-no-alpha.png
+fuzzy(9,40000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true)                                         == webgl-color-test.html?16bpp&________&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(9,40000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true)  == webgl-color-test.html?16bpp&readback&_______&alpha  wrapper.html?colors-non-premult.png
+fuzzy(9,40000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true)                                         == webgl-color-test.html?16bpp&________&premult&alpha  wrapper.html?colors-premult.png
+fuzzy(9,40000) skip-if(!(Android||B2G)) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true)  == webgl-color-test.html?16bpp&readback&premult&alpha  wrapper.html?colors-premult.png
 
 # Force native GL (Windows):
-skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-clear-test.html?native-gl        wrapper.html?green.png
-skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-orientation-test.html?native-gl  wrapper.html?white-top-left.png
-skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-color-test.html?native-gl        wrapper.html?colors.png
-skip-if(!winWidget) pref(webgl.disable-angle,true) pref(webgl.prefer-16bpp,true)  == webgl-color-test.html?native-gl&16bpp  wrapper.html?colors.png
+skip-if(!winWidget) pref(webgl.disable-angle,true)  == webgl-color-test.html?native-gl  wrapper.html?colors-no-alpha.png
 
 
 # Non-WebGL Reftests!
 
 # Do we correctly handle multiple clip paths?
 != clip-multiple-paths.html clip-multiple-paths-badref.html
 
 # Bug 815648
--- a/dom/canvas/test/reftest/webgl-clear-test.html
+++ b/dom/canvas/test/reftest/webgl-clear-test.html
@@ -1,50 +1,40 @@
 <!DOCTYPE html>
-<html class="reftest-wait">
-<head>
-<meta charset="UTF-8">
+<meta charset='UTF-8'>
+<!--
+Clear the canvas to green to test that we get pixels to the screen.
 
-<script type="text/javascript" src="webgl-utils.js"></script>
-<script type="text/javascript">
-/* Clear Test
- *
- * Clear the canvas to green to test that we get pixels to the screen.
- */
+If this fails, something is seriously wrong.
+-->
+<html>
 
-"use strict";
+<head>
+  <script type='text/javascript' src='webgl-utils.js'></script>
+  <script type='text/javascript'>
+'use strict';
 
-function renderGL(gl) {
-  gl.clearColor(0.0, 1.0, 0.0, 1.0);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-  gl.finish();
-}
-
-function renderFailure(canvas) {
-  // This will also trigger RAF for us.
-  var context = canvas.getContext("2d");
-  context.fillText('WebGL failed.', 64, 64);
+function setStatus(text) {
+  var elem = document.getElementById('status');
+  elem.innerHTML = text;
 }
 
 function runTest() {
-  var canvas = document.getElementById("canvas");
-  var gl = initGL(canvas);
-
-  if (gl)
-    renderGL(gl);
-  else
-    renderFailure(canvas);
+  var canvas = document.getElementById('canvas');
 
-  waitForComposite(testComplete);
-}
+  var gl = initGL(canvas);
+  if (!gl) {
+    setStatus('WebGL context creation failed.');
+    return;
+  }
 
-function testComplete() {
-  document.documentElement.removeAttribute("class");
+  gl.clearColor(0.0, 1.0, 0.0, 1.0);
+  gl.clear(gl.COLOR_BUFFER_BIT);
 }
-
-</script>
+  </script>
 </head>
 
-<body onload="rAF(runTest);">
-  <canvas id="canvas" width="256" height="256"></canvas>
+<body onload='runTest();'>
+  <canvas id='canvas' width='256' height='256'></canvas>
+  <div id='status'></div>
 </body>
 
 </html>
deleted file mode 100644
--- a/dom/canvas/test/reftest/webgl-color-alpha-test.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<head>
-<meta charset="UTF-8">
-
-<script type="text/javascript" src="webgl-utils.js"></script>
-<script type="text/javascript">
-/* Color-Alpha Test
- *
- * Clear the four quadrants of the canvas as follows:
- * +------+------+
- * | red  |green |
- * |      |      |
- * +------+------+
- * | blue |white |
- * |      |      |
- * +------+------+
- * However, unlike the Color test, clear with a given alpha value.
- * What effect this has depends on the context-creation args passed
- * to this page.
- *
- * Here we check that we handle various combinations of alpha and
- * premultipliedAlpha correctly.
- */
-
-"use strict";
-
-function renderGL(gl, value, alpha) {
-  gl.enable(gl.SCISSOR_TEST);
-
-  gl.scissor(0, 128, 128, 128);
-  gl.clearColor(value, 0.0, 0.0, alpha);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.scissor(128, 128, 128, 128);
-  gl.clearColor(0.0, value, 0.0, alpha);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.scissor(0, 0, 128, 128);
-  gl.clearColor(0.0, 0.0, value, alpha);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.scissor(128, 0, 128, 128);
-  gl.clearColor(value, value, value, alpha);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.finish();
-}
-
-function renderFailure(canvas) {
-  // This will also trigger RAF for us.
-  var context = canvas.getContext("2d");
-  context.fillText('WebGL failed.', 64, 64);
-}
-
-function runTest() {
-  var canvas = document.getElementById("canvas");
-  var gl = initGL(canvas);
-
-  var value = arg("colorVal");
-  var alpha = arg("alphaVal");
-
-  if (gl)
-    renderGL(gl, value, alpha);
-  else
-    renderFailure(canvas);
-
-  waitForComposite(testComplete);
-}
-
-function testComplete() {
-  document.documentElement.removeAttribute("class");
-}
-</script>
-</head>
-
-<body onload="rAF(runTest);">
-  <canvas id="canvas" width="256" height="256"></canvas>
-</body>
-
-</html>
--- a/dom/canvas/test/reftest/webgl-color-test.html
+++ b/dom/canvas/test/reftest/webgl-color-test.html
@@ -1,76 +1,123 @@
 <!DOCTYPE html>
-<html class="reftest-wait">
-<head>
-<meta charset="UTF-8">
+<meta charset='UTF-8'>
+<!--
+Color Test
+
+Clear the four quadrants of the canvas as follows:
++------+------+
+| blue |black |
+|      |      |
++------+------+
+| red  |green |
+|      |      |
++------+------+
 
-<script type="text/javascript" src="webgl-utils.js"></script>
-<script type="text/javascript">
-/* Color Test
- *
- * Clear the four quadrants of the canvas as follows:
- * +------+------+
- * | red  |green |
- * |      |      |
- * +------+------+
- * | blue |white |
- * |      |      |
- * +------+------+
- *
- * This is for checking that we're getting the right colors when
- * we ask for them. This combined with the Orientation test assure
- * that we are getting the correct colors in the correct places.
- */
+Clear with a given alpha value. What effect this has depends on the
+context-creation args passed to this page.
+-->
+<html class='reftest-wait'>
 
-"use strict";
+<head>
+  <script type='text/javascript' src='webgl-utils.js'></script>
+  <script type='text/javascript'>
+'use strict';
 
-function renderGL(gl) {
+var COLOR_VALUE = 127.0 / 255.0;
+var ALPHA_VALUE = 127.0 / 255.0;
+
+function renderFrame(gl) {
   gl.enable(gl.SCISSOR_TEST);
 
-  gl.scissor(0, 128, 128, 128);
-  gl.clearColor(1.0, 0.0, 0.0, 1.0);
+  gl.scissor(0, 0, 100, 100);
+  gl.clearColor(COLOR_VALUE, 0.0, 0.0, ALPHA_VALUE);
   gl.clear(gl.COLOR_BUFFER_BIT);
 
-  gl.scissor(128, 128, 128, 128);
-  gl.clearColor(0.0, 1.0, 0.0, 1.0);
+  gl.scissor(100, 0, 100, 100);
+  gl.clearColor(0.0, COLOR_VALUE, 0.0, ALPHA_VALUE);
   gl.clear(gl.COLOR_BUFFER_BIT);
 
-  gl.scissor(0, 0, 128, 128);
-  gl.clearColor(0.0, 0.0, 1.0, 1.0);
+  gl.scissor(0, 100, 100, 100);
+  gl.clearColor(0.0, 0.0, COLOR_VALUE, ALPHA_VALUE);
   gl.clear(gl.COLOR_BUFFER_BIT);
 
-  gl.scissor(128, 0, 128, 128);
-  gl.clearColor(1.0, 1.0, 1.0, 1.0);
+  gl.scissor(100, 100, 100, 100);
+  gl.clearColor(0.0, 0.0, 0.0, ALPHA_VALUE);
   gl.clear(gl.COLOR_BUFFER_BIT);
+}
 
-  gl.finish();
+////////////////////////////////////////////////////////////////////////////////
+// Boilerplate
+
+var TIMEOUT_MS = 10 * 1000;
+
+function setStatus(text) {
+  var elem = document.getElementById('status');
+  elem.innerHTML = text;
 }
 
-function renderFailure(canvas) {
-  // This will also trigger RAF for us.
-  var context = canvas.getContext("2d");
-  context.fillText('WebGL failed.', 64, 64);
+var gIsComplete = false;
+
+function markComplete(statusText) {
+  if (!statusText)
+    statusText = '';
+
+  if (gIsComplete)
+    return;
+  gIsComplete = true;
+
+  setStatus(statusText);
+  document.documentElement.removeAttribute('class');
+}
+
+function markError(text) {
+  markComplete('Error: ' + text);
+}
+
+function markTimedOut() {
+  markError('Timed out waiting on test completion.');
+}
+
+function runFrame(gl, frameCount, maxFrameCount) {
+  renderFrame(gl);
+  frameCount++;
+
+  if (frameCount >= maxFrameCount) {
+    console.log('Rendered ' + frameCount + ' frames.');
+    markComplete();
+    return;
+  }
+
+  requestAnimationFrame(function(){
+    runFrame(gl, frameCount, maxFrameCount);
+  });
 }
 
 function runTest() {
-  var canvas = document.getElementById("canvas");
-  var gl = initGL(canvas);
+  var canvas = document.getElementById('canvas');
 
-  if (gl)
-    renderGL(gl);
-  else
-    renderFailure(canvas);
+  var gl = initGL(canvas);
+  if (!gl) {
+    markError('WebGL context creation failed.');
+    return;
+  }
 
-  waitForComposite(testComplete);
-}
+  var maxFrameCount = arg('frame', 1);
+  if (maxFrameCount < 1) {
+    markError('Invalid `frame` arg: ' + maxFrameCount);
+    return;
+  }
 
-function testComplete() {
-  document.documentElement.removeAttribute("class");
+  setStatus('Waiting...');
+
+  runFrame(gl, 0, maxFrameCount);
+  setTimeout(markTimedOut, TIMEOUT_MS);
 }
-</script>
+  </script>
 </head>
 
-<body onload="rAF(runTest);">
-  <canvas id="canvas" width="256" height="256"></canvas>
+<body onload='runTest();'>
+  <canvas id='canvas' width='200' height='200'></canvas>
+  <div id='status'></div>
 </body>
 
 </html>
deleted file mode 100644
--- a/dom/canvas/test/reftest/webgl-orientation-test.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<head>
-<meta charset="UTF-8">
-
-<script type="text/javascript" src="webgl-utils.js"></script>
-<script type="text/javascript">
-/* Orientation Test
- *
- * Clear the canvas to black, and clear the upper-left quadrant
- * to white. If this doesn't pass, but the Clear test does, then
- * likely y-flip is wrong.
- */
-
-"use strict";
-
-function renderGL(gl) {
-  gl.clearColor(0.0, 0.0, 0.0, 1.0);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.enable(gl.SCISSOR_TEST);
-  gl.scissor(0, 128, 128, 128);
-  gl.clearColor(1.0, 1.0, 1.0, 1.0);
-  gl.clear(gl.COLOR_BUFFER_BIT);
-
-  gl.finish();
-}
-
-function renderFailure(canvas) {
-  // This will also trigger RAF for us.
-  var context = canvas.getContext("2d");
-  context.fillText('WebGL failed.', 64, 64);
-}
-
-function runTest() {
-  var canvas = document.getElementById("canvas");
-  var gl = initGL(canvas);
-
-  if (gl)
-    renderGL(gl);
-  else
-    renderFailure(canvas);
-
-  waitForComposite(testComplete);
-}
-
-function testComplete() {
-  document.documentElement.removeAttribute("class");
-}
-</script>
-</head>
-
-<body onload="rAF(runTest);">
-  <canvas id="canvas" width="256" height="256"></canvas>
-</body>
-
-</html>
--- a/dom/canvas/test/reftest/webgl-utils.js
+++ b/dom/canvas/test/reftest/webgl-utils.js
@@ -17,40 +17,39 @@ function parseArgs() {
 
     args[key] = value;
   }
 
   return args;
 }
 
 var gArgs = null;
-function arg(key) {
+function arg(key, defaultVal) {
   if (gArgs === null) {
     gArgs = parseArgs();
   }
 
-  var ret = gArgs[key];
-  if (ret === undefined)
-    ret = false;
+  if (!(key in gArgs))
+    return defaultVal;
 
-  return ret;
+  return gArgs[key];
 }
 
 function initGL(canvas) {
   if (arg("nogl"))
     return null;
 
   var gl = null;
 
-  var withAA = arg("aa");
-  var withAlpha = arg("alpha");
-  var withDepth = arg("depth");
-  var withPremult = arg("premult");
-  var withPreserve = arg("preserve");
-  var withStencil = arg("stencil");
+  var withAA = arg("aa", false);
+  var withAlpha = arg("alpha", false);
+  var withDepth = arg("depth", false);
+  var withPremult = arg("premult", false);
+  var withPreserve = arg("preserve", false);
+  var withStencil = arg("stencil", false);
 
   try {
     var argDict = {
       alpha: withAlpha,
       depth: withDepth,
       stencil: withStencil,
       antialias: withAA,
       premultipliedAlpha: withPremult,
deleted file mode 100644
index 224b193a5ec4d4f76d7dfc5bb8fa843336970a5f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -966,18 +966,20 @@ void
 FetchBody<Derived>::BeginConsumeBodyMainThread()
 {
   AssertIsOnMainThread();
   AutoFailConsumeBody<Derived> autoReject(DerivedClass());
   nsresult rv;
   nsCOMPtr<nsIInputStream> stream;
   DerivedClass()->GetBody(getter_AddRefs(stream));
   if (!stream) {
-    NS_WARNING("Could not get stream");
-    return;
+    rv = NS_NewCStringInputStream(getter_AddRefs(stream), EmptyCString());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
   }
 
   nsCOMPtr<nsIInputStreamPump> pump;
   rv = NS_NewInputStreamPump(getter_AddRefs(pump),
                              stream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -9,33 +9,36 @@
 #include "nsIOutputStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIUploadChannel2.h"
 
 #include "nsContentPolicyUtils.h"
+#include "nsCORSListenerProxy.h"
 #include "nsDataHandler.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
 
 #include "Fetch.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener)
+NS_IMPL_ISUPPORTS(FetchDriver,
+                  nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
+                  nsIAsyncVerifyRedirectCallback)
 
 FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                          nsILoadGroup* aLoadGroup)
   : mPrincipal(aPrincipal)
   , mLoadGroup(aLoadGroup)
   , mRequest(aRequest)
   , mFetchRecursionCount(0)
   , mResponseAvailableCalled(false)
@@ -83,23 +86,25 @@ FetchDriver::Fetch(bool aCORSFlag)
 nsresult
 FetchDriver::ContinueFetch(bool aCORSFlag)
 {
   workers::AssertIsOnMainThread();
 
   nsAutoCString url;
   mRequest->GetURL(url);
   nsCOMPtr<nsIURI> requestURI;
-  // FIXME(nsm): Deal with relative URLs.
   nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
                           nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return FailWithNetworkError();
   }
 
+  // Begin Step 4 of the Fetch algorithm
+  // https://fetch.spec.whatwg.org/#fetching
+
   // FIXME(nsm): Bug 1039846: Add CSP checks
 
   nsAutoCString scheme;
   rv = requestURI->GetScheme(scheme);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return FailWithNetworkError();
   }
 
@@ -120,17 +125,17 @@ FetchDriver::ContinueFetch(bool aCORSFla
   }
 
   if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
     return FailWithNetworkError();
   }
 
   bool corsPreflight = false;
   if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
-      (mRequest->UnsafeRequest() && (mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
+      (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
     corsPreflight = true;
   }
 
   mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
   return HttpFetch(true /* aCORSFlag */, corsPreflight);
 }
 
 nsresult
@@ -269,56 +274,24 @@ FetchDriver::BasicFetch()
   } else if (scheme.LowerCaseEqualsLiteral("http") ||
              scheme.LowerCaseEqualsLiteral("https")) {
     return HttpFetch();
   }
 
   return FailWithNetworkError();
 }
 
+// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
+// Functionality is often split between here, the CORS listener proxy and the
+// Necko HTTP implementation.
 nsresult
-FetchDriver::HttpFetch(bool aCORSFlag, bool aPreflightCORSFlag, bool aAuthenticationFlag)
-{
-  mResponse = nullptr;
-
-  // XXXnsm: The ServiceWorker interception should happen automatically.
-  return ContinueHttpFetchAfterServiceWorker();
-}
-
-nsresult
-FetchDriver::ContinueHttpFetchAfterServiceWorker()
+FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthenticationFlag)
 {
-  if (!mResponse) {
-    // FIXME(nsm): Set skip SW flag.
-    // FIXME(nsm): Deal with CORS flags cases which will also call
-    // ContinueHttpFetchAfterCORSPreflight().
-    return ContinueHttpFetchAfterCORSPreflight();
-  }
-
-  // Otherwise ServiceWorker replied with a response.
-  return ContinueHttpFetchAfterNetworkFetch();
-}
-
-nsresult
-FetchDriver::ContinueHttpFetchAfterCORSPreflight()
-{
-  // mResponse is currently the CORS response.
-  // We may have to pass it via argument.
-  if (mResponse && mResponse->IsError()) {
-    return FailWithNetworkError();
-  }
-
-  return HttpNetworkFetch();
-}
-
-nsresult
-FetchDriver::HttpNetworkFetch()
-{
-  // We don't create a HTTPRequest copy since Necko sets the information on the
-  // nsIHttpChannel instead.
+  // Step 1. "Let response be null."
+  mResponse = nullptr;
 
   nsresult rv;
 
   nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
@@ -331,16 +304,23 @@ FetchDriver::HttpNetworkFetch()
                           nullptr,
                           nullptr,
                           ios);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
+  // Step 2 deals with letting ServiceWorkers intercept requests. This is
+  // handled by Necko after the channel is opened.
+  // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
+  // set based on the Request's flag.
+
+  // From here on we create a channel and set its properties with the
+  // information from the InternalRequest. This is an implementation detail.
   MOZ_ASSERT(mLoadGroup);
   nsCOMPtr<nsIChannel> chan;
   rv = NS_NewChannel(getter_AddRefs(chan),
                      uri,
                      mPrincipal,
                      nsILoadInfo::SEC_NORMAL,
                      mRequest->GetContext(),
                      mLoadGroup,
@@ -348,82 +328,165 @@ FetchDriver::HttpNetworkFetch()
                      nsIRequest::LOAD_NORMAL,
                      ios);
   mLoadGroup = nullptr;
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
+  // Insert ourselves into the notification callbacks chain so we can handle
+  // cross-origin redirects.
+  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
+  chan->SetNotificationCallbacks(this);
+
+  // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
+  // true..." is handled by the CORS proxy.
+  //
+  // Step 3.2 "Set request's skip service worker flag." This isn't required
+  // since Necko will fall back to the network if the ServiceWorker does not
+  // respond with a valid Response.
+  //
+  // NS_StartCORSPreflight() will automatically kick off the original request
+  // if it succeeds, so we need to have everything setup for the original
+  // request too.
+
+  // Step 3.3 "Let credentials flag be set if either request's credentials mode
+  // is include, or request's credentials mode is same-origin and the CORS flag
+  // is unset, and unset otherwise."
+  bool useCredentials = false;
+  if (mRequest->GetCredentialsMode() == RequestCredentials::Include ||
+      (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag)) {
+    useCredentials = true;
+  }
+
+  // FIXME(nsm): Bug 1120715.
+  // Step 3.4 "If request's cache mode is default and request's header list
+  // contains a header named `If-Modified-Since`, `If-None-Match`,
+  // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
+  // to no-store."
+
+  // Step 3.5 begins "HTTP network or cache fetch".
+  // HTTP network or cache fetch
+  // ---------------------------
+  // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
   nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
   if (httpChan) {
+    // Copy the method.
     nsAutoCString method;
     mRequest->GetMethod(method);
     rv = httpChan->SetRequestMethod(method);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       FailWithNetworkError();
       return rv;
     }
 
+    // Set the same headers.
     nsAutoTArray<InternalHeaders::Entry, 5> headers;
     mRequest->Headers()->GetEntries(headers);
     for (uint32_t i = 0; i < headers.Length(); ++i) {
       httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
     }
 
+    // Step 2. Set the referrer. This is handled better in Bug 1112922.
     MOZ_ASSERT(mRequest->ReferrerIsURL());
     nsCString referrer = mRequest->ReferrerAsURL();
     if (!referrer.IsEmpty()) {
       nsCOMPtr<nsIURI> uri;
       rv = NS_NewURI(getter_AddRefs(uri), referrer, nullptr, nullptr, ios);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return FailWithNetworkError();
       }
       rv = httpChan->SetReferrer(uri);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return FailWithNetworkError();
       }
     }
 
+    // Step 3 "If HTTPRequest's force Origin header flag is set..."
     if (mRequest->ForceOriginHeader()) {
       nsAutoString origin;
       rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        FailWithNetworkError();
-        return rv;
+        return FailWithNetworkError();
       }
       httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
                                  NS_ConvertUTF16toUTF8(origin),
                                  false /* merge */);
     }
+    // Bug 1120722 - Authorization will be handled later.
+    // Auth may require prompting, we don't support it yet.
+    // The next patch in this same bug prevents this from aborting the request.
+    // Credentials checks for CORS are handled by nsCORSListenerProxy,
   }
 
+  // Step 5. Proxy authentication will be handled by Necko.
+  // FIXME(nsm): Bug 1120715.
+  // Step 7-10. "If request's cache mode is neither no-store nor reload..."
+
+  // Continue setting up 'HTTPRequest'. Content-Type and body data.
   nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
   if (uploadChan) {
     nsAutoCString contentType;
     ErrorResult result;
     mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
     // This is an error because the Request constructor explicitly extracts and
     // sets a content-type per spec.
     if (result.Failed()) {
-      return result.ErrorCode();
+      return FailWithNetworkError();
     }
 
     nsCOMPtr<nsIInputStream> bodyStream;
     mRequest->GetBody(getter_AddRefs(bodyStream));
     if (bodyStream) {
       nsAutoCString method;
       mRequest->GetMethod(method);
       rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return FailWithNetworkError();
       }
     }
   }
-  return chan->AsyncOpen(this, nullptr);
+
+  // Set up a CORS proxy that will handle the various requirements of the CORS
+  // protocol. It handles the preflight cache and CORS response headers.
+  // If the request is allowed, it will start our original request
+  // and our observer will be notified. On failure, our observer is notified
+  // directly.
+  nsRefPtr<nsCORSListenerProxy> corsListener =
+    new nsCORSListenerProxy(this, mPrincipal, useCredentials);
+  rv = corsListener->Init(chan, true /* allow data uri */);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FailWithNetworkError();
+  }
+
+  // If preflight is required, start a "CORS preflight fetch"
+  // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
+  // implementation is handled by NS_StartCORSPreflight, we just set up the
+  // unsafeHeaders so they can be verified against the response's
+  // "Access-Control-Allow-Headers" header.
+  if (aCORSPreflightFlag) {
+    nsCOMPtr<nsIChannel> preflightChannel;
+    nsAutoTArray<nsCString, 5> unsafeHeaders;
+    mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
+
+    rv = NS_StartCORSPreflight(chan, corsListener, mPrincipal,
+                               useCredentials,
+                               unsafeHeaders,
+                               getter_AddRefs(preflightChannel));
+  } else {
+   rv = chan->AsyncOpen(corsListener, nullptr);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FailWithNetworkError();
+  }
+
+  // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
+  return NS_OK;
 }
 
 nsresult
 FetchDriver::ContinueHttpFetchAfterNetworkFetch()
 {
   workers::AssertIsOnMainThread();
   MOZ_ASSERT(mResponse);
   MOZ_ASSERT(!mResponse->IsError());
@@ -452,43 +515,51 @@ FetchDriver::BeginAndGetFilteredResponse
     case InternalRequest::RESPONSETAINT_OPAQUE:
       filteredResponse = InternalResponse::OpaqueResponse();
       break;
     default:
       MOZ_CRASH("Unexpected case");
   }
 
   MOZ_ASSERT(filteredResponse);
+  MOZ_ASSERT(mObserver);
   mObserver->OnResponseAvailable(filteredResponse);
   mResponseAvailableCalled = true;
   return filteredResponse.forget();
 }
 
 void
 FetchDriver::BeginResponse(InternalResponse* aResponse)
 {
   nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse);
   // Release the ref.
 }
 
 nsresult
 FetchDriver::SucceedWithResponse()
 {
-  mObserver->OnResponseEnd();
+  workers::AssertIsOnMainThread();
+  if (mObserver) {
+    mObserver->OnResponseEnd();
+    mObserver = nullptr;
+  }
   return NS_OK;
 }
 
 nsresult
 FetchDriver::FailWithNetworkError()
 {
-  MOZ_ASSERT(mObserver);
+  workers::AssertIsOnMainThread();
   nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
-  mObserver->OnResponseAvailable(error);
-  mResponseAvailableCalled = true;
-  mObserver->OnResponseEnd();
+  if (mObserver) {
+    mObserver->OnResponseAvailable(error);
+    mResponseAvailableCalled = true;
+    mObserver->OnResponseEnd();
+    mObserver = nullptr;
+  }
   return NS_OK;
 }
 
 namespace {
 class FillResponseHeaders MOZ_FINAL : public nsIHttpHeaderVisitor {
   InternalResponse* mResponse;
 
   ~FillResponseHeaders()
@@ -512,17 +583,19 @@ public:
 
 NS_IMPL_ISUPPORTS(FillResponseHeaders, nsIHttpHeaderVisitor)
 } // anonymous namespace
 
 NS_IMETHODIMP
 FetchDriver::OnStartRequest(nsIRequest* aRequest,
                             nsISupports* aContext)
 {
+  workers::AssertIsOnMainThread();
   MOZ_ASSERT(!mPipeOutputStream);
+  MOZ_ASSERT(mObserver);
   nsresult rv;
   aRequest->GetStatus(&rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
   nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest);
@@ -602,22 +675,176 @@ FetchDriver::OnDataAvailable(nsIRequest*
   return rv;
 }
 
 NS_IMETHODIMP
 FetchDriver::OnStopRequest(nsIRequest* aRequest,
                            nsISupports* aContext,
                            nsresult aStatusCode)
 {
-  MOZ_ASSERT(mPipeOutputStream);
-  mPipeOutputStream->Close();
+  workers::AssertIsOnMainThread();
+  if (mPipeOutputStream) {
+    mPipeOutputStream->Close();
+  }
 
   if (NS_FAILED(aStatusCode)) {
     FailWithNetworkError();
     return aStatusCode;
   }
 
   ContinueHttpFetchAfterNetworkFetch();
   return NS_OK;
 }
 
+// This is called when the channel is redirected.
+NS_IMETHODIMP
+FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                    nsIChannel* aNewChannel,
+                                    uint32_t aFlags,
+                                    nsIAsyncVerifyRedirectCallback *aCallback)
+{
+  NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
+
+  nsresult rv;
+
+  // Section 4.2, Step 4.6-4.7, enforcing a redirect count is done by Necko.
+  // The pref used is "network.http.redirection-limit" which is set to 20 by
+  // default.
+  //
+  // Step 4.8. We only unset this for spec compatibility. Any actions we take
+  // on mRequest here do not affect what the channel does.
+  mRequest->UnsetSameOriginDataURL();
+
+  //
+  // Requests that require preflight are not permitted to redirect.
+  // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual
+  // redirect flag to decide whether to execute step 4.10 or not. We do not
+  // represent it in our implementation.
+  // The only thing we do is to check if the request requires a preflight (part
+  // of step 4.9), in which case we abort. This part cannot be done by
+  // nsCORSListenerProxy since it does not have access to mRequest.
+  // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all
+  // the other steps are handled by nsCORSListenerProxy.
+  if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
+    rv = DoesNotRequirePreflight(aNewChannel);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
+                 "DoesNotRequirePreflight returned failure");
+      return rv;
+    }
+  }
+
+  mRedirectCallback = aCallback;
+  mOldRedirectChannel = aOldChannel;
+  mNewRedirectChannel = aNewChannel;
+
+  nsCOMPtr<nsIChannelEventSink> outer =
+    do_GetInterface(mNotificationCallbacks);
+  if (outer) {
+    // The callee is supposed to call OnRedirectVerifyCallback() on success,
+    // and nobody has to call it on failure, so we can just return after this
+    // block.
+    rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
+    if (NS_FAILED(rv)) {
+      aOldChannel->Cancel(rv);
+      mRedirectCallback = nullptr;
+      mOldRedirectChannel = nullptr;
+      mNewRedirectChannel = nullptr;
+    }
+    return rv;
+  }
+
+  (void) OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+// Returns NS_OK if no preflight is required, error otherwise.
+nsresult
+FetchDriver::DoesNotRequirePreflight(nsIChannel* aChannel)
+{
+  // If this is a same-origin request or the channel's URI inherits
+  // its principal, it's allowed.
+  if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
+    return NS_OK;
+  }
+
+  // Check if we need to do a preflight request.
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
+
+  nsAutoCString method;
+  httpChannel->GetRequestMethod(method);
+  if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
+      !mRequest->Headers()->HasOnlySimpleHeaders() ||
+      (!method.LowerCaseEqualsLiteral("get") &&
+       !method.LowerCaseEqualsLiteral("post") &&
+       !method.LowerCaseEqualsLiteral("head"))) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
+{
+  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+    *aResult = static_cast<nsIChannelEventSink*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+
+  nsresult rv;
+
+  if (mNotificationCallbacks) {
+    rv = mNotificationCallbacks->GetInterface(aIID, aResult);
+    if (NS_SUCCEEDED(rv)) {
+      NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
+      return rv;
+    }
+  }
+  else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
+    *aResult = static_cast<nsIStreamListener*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+  else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
+    *aResult = static_cast<nsIRequestObserver*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+
+  return QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+FetchDriver::OnRedirectVerifyCallback(nsresult aResult)
+{
+  // On a successful redirect we perform the following substeps of Section 4.2,
+  // step 4.10.
+  if (NS_SUCCEEDED(aResult)) {
+    // Step 4.10.3 "Set request's url to locationURL." so that when we set the
+    // Response's URL from the Request's URL in Section 4, step 6, we get the
+    // final value.
+    nsCOMPtr<nsIURI> newURI;
+    nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aResult = rv;
+    } else {
+      // We need to update our request's URL.
+      nsAutoCString newUrl;
+      newURI->GetSpec(newUrl);
+      mRequest->SetURL(newUrl);
+    }
+  }
+
+  if (NS_FAILED(aResult)) {
+    mOldRedirectChannel->Cancel(aResult);
+  }
+
+  mOldRedirectChannel = nullptr;
+  mNewRedirectChannel = nullptr;
+  mRedirectCallback->OnRedirectVerifyCallback(aResult);
+  mRedirectCallback = nullptr;
+  return NS_OK;
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -2,16 +2,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FetchDriver_h
 #define mozilla_dom_FetchDriver_h
 
 #include "nsAutoPtr.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
 #include "nsIStreamListener.h"
 #include "nsRefPtr.h"
 
 #include "mozilla/DebugOnly.h"
 
 class nsIOutputStream;
 class nsILoadGroup;
 class nsIPrincipal;
@@ -30,57 +33,65 @@ public:
   virtual void OnResponseAvailable(InternalResponse* aResponse) = 0;
   virtual void OnResponseEnd() = 0;
 
 protected:
   virtual ~FetchDriverObserver()
   { };
 };
 
-class FetchDriver MOZ_FINAL : public nsIStreamListener
+class FetchDriver MOZ_FINAL : public nsIStreamListener,
+                              public nsIChannelEventSink,
+                              public nsIInterfaceRequestor,
+                              public nsIAsyncVerifyRedirectCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
   explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                        nsILoadGroup* aLoadGroup);
   NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
 
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsRefPtr<InternalRequest> mRequest;
   nsRefPtr<InternalResponse> mResponse;
   nsCOMPtr<nsIOutputStream> mPipeOutputStream;
   nsRefPtr<FetchDriverObserver> mObserver;
+  nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel> mOldRedirectChannel;
+  nsCOMPtr<nsIChannel> mNewRedirectChannel;
   uint32_t mFetchRecursionCount;
 
   DebugOnly<bool> mResponseAvailableCalled;
 
   FetchDriver() = delete;
   FetchDriver(const FetchDriver&) = delete;
   FetchDriver& operator=(const FetchDriver&) = delete;
   ~FetchDriver();
 
   nsresult Fetch(bool aCORSFlag);
   nsresult ContinueFetch(bool aCORSFlag);
   nsresult BasicFetch();
-  nsresult HttpFetch(bool aCORSFlag = false, bool aPreflightCORSFlag = false, bool aAuthenticationFlag = false);
-  nsresult ContinueHttpFetchAfterServiceWorker();
-  nsresult ContinueHttpFetchAfterCORSPreflight();
-  nsresult HttpNetworkFetch();
+  nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false);
   nsresult ContinueHttpFetchAfterNetworkFetch();
   // Returns the filtered response sent to the observer.
   already_AddRefed<InternalResponse>
   BeginAndGetFilteredResponse(InternalResponse* aResponse);
   // Utility since not all cases need to do any post processing of the filtered
   // response.
   void BeginResponse(InternalResponse* aResponse);
   nsresult FailWithNetworkError();
   nsresult SucceedWithResponse();
+  nsresult DoesNotRequirePreflight(nsIChannel* aChannel);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FetchDriver_h
--- a/dom/fetch/InternalHeaders.cpp
+++ b/dom/fetch/InternalHeaders.cpp
@@ -298,38 +298,68 @@ InternalHeaders::BasicHeaders(InternalHe
 
 // static
 already_AddRefed<InternalHeaders>
 InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
 {
   nsRefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
   ErrorResult result;
 
-  nsAutoTArray<nsCString, 1> acExposedNames;
-  aHeaders->GetAll(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
+  nsAutoCString acExposedNames;
+  aHeaders->Get(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
   MOZ_ASSERT(!result.Failed());
 
+  nsAutoTArray<nsCString, 5> exposeNamesArray;
+  nsCCharSeparatedTokenizer exposeTokens(acExposedNames, ',');
+  while (exposeTokens.hasMoreTokens()) {
+    const nsDependentCSubstring& token = exposeTokens.nextToken();
+    if (token.IsEmpty()) {
+      continue;
+    }
+
+    if (!NS_IsValidHTTPToken(token)) {
+      NS_WARNING("Got invalid HTTP token in Access-Control-Expose-Headers. Header value is:");
+      NS_WARNING(acExposedNames.get());
+      exposeNamesArray.Clear();
+      break;
+    }
+
+    exposeNamesArray.AppendElement(token);
+  }
+
   nsCaseInsensitiveCStringArrayComparator comp;
   for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
     const Entry& entry = aHeaders->mList[i];
     if (entry.mName.EqualsASCII("cache-control") ||
         entry.mName.EqualsASCII("content-language") ||
         entry.mName.EqualsASCII("content-type") ||
         entry.mName.EqualsASCII("expires") ||
         entry.mName.EqualsASCII("last-modified") ||
         entry.mName.EqualsASCII("pragma") ||
-        acExposedNames.Contains(entry.mName, comp)) {
+        exposeNamesArray.Contains(entry.mName, comp)) {
       cors->Append(entry.mName, entry.mValue, result);
       MOZ_ASSERT(!result.Failed());
     }
   }
 
   return cors.forget();
 }
 
 void
 InternalHeaders::GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const
 {
   MOZ_ASSERT(aEntries.IsEmpty());
   aEntries.AppendElements(mList);
 }
+
+void
+InternalHeaders::GetUnsafeHeaders(nsTArray<nsCString>& aNames) const
+{
+  MOZ_ASSERT(aNames.IsEmpty());
+  for (uint32_t i = 0; i < mList.Length(); ++i) {
+    const Entry& header = mList[i];
+    if (!InternalHeaders::IsSimpleHeader(header.mName, header.mValue)) {
+      aNames.AppendElement(header.mName);
+    }
+  }
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalHeaders.h
+++ b/dom/fetch/InternalHeaders.h
@@ -84,21 +84,22 @@ public:
   static already_AddRefed<InternalHeaders>
   BasicHeaders(InternalHeaders* aHeaders);
 
   static already_AddRefed<InternalHeaders>
   CORSHeaders(InternalHeaders* aHeaders);
 
   void
   GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const;
+
+  void
+  GetUnsafeHeaders(nsTArray<nsCString>& aNames) const;
 private:
   virtual ~InternalHeaders();
 
-  static bool IsSimpleHeader(const nsACString& aName,
-                             const nsACString& aValue);
   static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
   static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
   bool IsImmutable(ErrorResult& aRv) const;
   bool IsForbiddenRequestHeader(const nsACString& aName) const;
   bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const;
   bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
                                       const nsACString& aValue) const;
   bool IsForbiddenResponseHeader(const nsACString& aName) const;
@@ -115,14 +116,17 @@ private:
   {
     return IsInvalidName(aName, aRv) ||
            IsInvalidValue(aValue, aRv) ||
            IsImmutable(aRv) ||
            IsForbiddenRequestHeader(aName) ||
            IsForbiddenRequestNoCorsHeader(aName, aValue) ||
            IsForbiddenResponseHeader(aName);
   }
+
+  static bool IsSimpleHeader(const nsACString& aName,
+                             const nsACString& aValue);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_InternalHeaders_h
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -20,19 +20,26 @@ namespace dom {
 // The global is used to extract the principal.
 already_AddRefed<InternalRequest>
 InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
 {
   nsRefPtr<InternalRequest> copy = new InternalRequest();
   copy->mURL.Assign(mURL);
   copy->SetMethod(mMethod);
   copy->mHeaders = new InternalHeaders(*mHeaders);
+  copy->SetUnsafeRequest();
 
   copy->mBodyStream = mBodyStream;
+  copy->mForceOriginHeader = true;
+  // The "client" is not stored in our implementation. Fetch API users should
+  // use the appropriate window/document/principal and other Gecko security
+  // mechanisms as appropriate.
+  copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
+  // The default referrer is already about:client.
 
   copy->mContext = nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
   copy->mCacheMode = mCacheMode;
   return copy.forget();
 }
 
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -57,20 +57,18 @@ public:
     : mMethod("GET")
     , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
     , mContextFrameType(FRAMETYPE_NONE)
     , mReferrerType(REFERRER_CLIENT)
     , mMode(RequestMode::No_cors)
     , mCredentialsMode(RequestCredentials::Omit)
     , mResponseTainting(RESPONSETAINT_BASIC)
     , mCacheMode(RequestCache::Default)
-    , mRedirectCount(0)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
-    , mManualRedirect(false)
     , mPreserveContentCodings(false)
       // FIXME(nsm): This should be false by default, but will lead to the
       // algorithm never loading data: URLs right now. See Bug 1018872 about
       // how certain contexts will override it to set it to true. Fetch
       // specification does not handle this yet.
     , mSameOriginDataURL(true)
     , mSkipServiceWorker(false)
     , mSynchronous(false)
@@ -87,20 +85,18 @@ public:
     , mContext(aOther.mContext)
     , mContextFrameType(aOther.mContextFrameType)
     , mReferrerType(aOther.mReferrerType)
     , mReferrerURL(aOther.mReferrerURL)
     , mMode(aOther.mMode)
     , mCredentialsMode(aOther.mCredentialsMode)
     , mResponseTainting(aOther.mResponseTainting)
     , mCacheMode(aOther.mCacheMode)
-    , mRedirectCount(aOther.mRedirectCount)
     , mAuthenticationFlag(aOther.mAuthenticationFlag)
     , mForceOriginHeader(aOther.mForceOriginHeader)
-    , mManualRedirect(aOther.mManualRedirect)
     , mPreserveContentCodings(aOther.mPreserveContentCodings)
     , mSameOriginDataURL(aOther.mSameOriginDataURL)
     , mSandboxedStorageAreaURLs(aOther.mSandboxedStorageAreaURLs)
     , mSkipServiceWorker(aOther.mSkipServiceWorker)
     , mSynchronous(aOther.mSynchronous)
     , mUnsafeRequest(aOther.mUnsafeRequest)
     , mUseURLCredentials(aOther.mUseURLCredentials)
   {
@@ -127,16 +123,22 @@ public:
   }
 
   void
   GetURL(nsCString& aURL) const
   {
     aURL.Assign(mURL);
   }
 
+  void
+  SetURL(const nsACString& aURL)
+  {
+    mURL.Assign(aURL);
+  }
+
   bool
   ReferrerIsNone() const
   {
     return mReferrerType == REFERRER_NONE;
   }
 
   bool
   ReferrerIsURL() const
@@ -179,16 +181,22 @@ public:
   }
 
   void
   SetMode(RequestMode aMode)
   {
     mMode = aMode;
   }
 
+  RequestCredentials
+  GetCredentialsMode() const
+  {
+    return mCredentialsMode;
+  }
+
   void
   SetCredentialsMode(RequestCredentials aCredentialsMode)
   {
     mCredentialsMode = aCredentialsMode;
   }
 
   ResponseTainting
   GetResponseTainting() const
@@ -215,16 +223,22 @@ public:
   }
 
   bool
   UnsafeRequest() const
   {
     return mUnsafeRequest;
   }
 
+  void
+  SetUnsafeRequest()
+  {
+    mUnsafeRequest = true;
+  }
+
   InternalHeaders*
   Headers()
   {
     return mHeaders;
   }
 
   bool
   ForceOriginHeader()
@@ -234,16 +248,22 @@ public:
 
   bool
   SameOriginDataURL() const
   {
     return mSameOriginDataURL;
   }
 
   void
+  UnsetSameOriginDataURL()
+  {
+    mSameOriginDataURL = false;
+  }
+
+  void
   SetBody(nsIInputStream* aStream)
   {
     // A request's body may not be reset once set.
     MOZ_ASSERT(!mBodyStream);
     mBodyStream = aStream;
   }
 
   // Will return the original stream!
@@ -257,22 +277,16 @@ public:
 
   // The global is used as the client for the new object.
   already_AddRefed<InternalRequest>
   GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const;
 
 private:
   ~InternalRequest();
 
-  void
-  SetURL(const nsACString& aURL)
-  {
-    mURL.Assign(aURL);
-  }
-
   nsCString mMethod;
   nsCString mURL;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   // nsContentPolicyType does not cover the complete set defined in the spec,
   // but it is a good start.
   nsContentPolicyType mContext;
@@ -283,21 +297,18 @@ private:
   // When mReferrerType is REFERRER_URL.
   nsCString mReferrerURL;
 
   RequestMode mMode;
   RequestCredentials mCredentialsMode;
   ResponseTainting mResponseTainting;
   RequestCache mCacheMode;
 
-  uint32_t mRedirectCount;
-
   bool mAuthenticationFlag;
   bool mForceOriginHeader;
-  bool mManualRedirect;
   bool mPreserveContentCodings;
   bool mSameOriginDataURL;
   bool mSandboxedStorageAreaURLs;
   bool mSkipServiceWorker;
   bool mSynchronous;
   bool mUnsafeRequest;
   bool mUseURLCredentials;
 };
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -8,20 +8,23 @@
 #include "nsISupportsImpl.h"
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/FetchBinding.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/URL.h"
+#include "mozilla/dom/workers/bindings/URL.h"
 
 #include "nsDOMString.h"
 
 #include "InternalResponse.h"
+#include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
 
@@ -47,22 +50,74 @@ Response::Error(const GlobalObject& aGlo
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
   nsRefPtr<Response> r = new Response(global, error);
   return r.forget();
 }
 
 /* static */ already_AddRefed<Response>
 Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
-                   uint16_t aStatus)
+                   uint16_t aStatus, ErrorResult& aRv)
 {
-  ErrorResult result;
+  nsAutoString parsedURL;
+
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+    nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
+    nsAutoCString spec;
+    aRv = docURI->GetSpec(spec);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    nsRefPtr<mozilla::dom::URL> url =
+      dom::URL::Constructor(aGlobal, aUrl, NS_ConvertUTF8toUTF16(spec), aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+
+    url->Stringify(parsedURL, aRv);
+  } else {
+    workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+
+    NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
+    nsRefPtr<workers::URL> url =
+      workers::URL::Constructor(aGlobal, aUrl, baseURL, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+
+    url->Stringify(parsedURL, aRv);
+  }
+
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (aStatus != 301 && aStatus != 302 && aStatus != 303 && aStatus != 307 && aStatus != 308) {
+    aRv.Throw(NS_ERROR_RANGE_ERR);
+    return nullptr;
+  }
+
+  Optional<ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams> body;
   ResponseInit init;
-  Optional<ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams> body;
-  nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, result);
+  init.mStatus = aStatus;
+  nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  r->GetInternalHeaders()->Set(NS_LITERAL_CSTRING("Location"),
+                               NS_ConvertUTF16toUTF8(parsedURL), aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
   return r.forget();
 }
 
 /*static*/ already_AddRefed<Response>
 Response::Constructor(const GlobalObject& aGlobal,
                       const Optional<ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams>& aBody,
                       const ResponseInit& aInit, ErrorResult& aRv)
 {
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -78,17 +78,17 @@ public:
 
   void
   GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
 
   static already_AddRefed<Response>
   Error(const GlobalObject& aGlobal);
 
   static already_AddRefed<Response>
-  Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus);
+  Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus, ErrorResult& aRv);
 
   static already_AddRefed<Response>
   Constructor(const GlobalObject& aGlobal,
               const Optional<ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams>& aBody,
               const ResponseInit& aInit, ErrorResult& rv);
 
   nsIGlobalObject* GetParentObject() const
   {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1266,17 +1266,18 @@ HTMLInputElement::Clone(mozilla::dom::No
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       if (mValueChanged) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         nsAutoString value;
         GetValueInternal(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
-        it->SetValueInternal(value, false, true);
+        rv = it->SetValueInternal(value, false, true);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
       break;
     case VALUE_MODE_FILENAME:
       if (it->OwnerDoc()->IsStaticDocument()) {
         // We're going to be used in print preview.  Since the doc is static
         // we can just grab the pretty string and use it as wallpaper
         GetDisplayFileName(it->mStaticDocFileList);
       } else {
@@ -1441,39 +1442,42 @@ HTMLInputElement::AfterSetAttr(int32_t a
         // prevented there being a valid step in range). Changing @max to/from
         // 1 and a number greater than on equal to 3 should change whether we
         // have a step mismatch or not.
         // The value may also need to change between a value that results in
         // a step mismatch and a value that results in overflow. For example,
         // if @max in the example above were to change from 1 to -1.
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::step) {
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        SetValueInternal(value, false, false);
+        nsresult rv = SetValueInternal(value, false, false);
+        NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::dir &&
                aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
       SetDirectionIfAuto(true, aNotify);
     } else if (aName == nsGkAtoms::lang) {
       if (mType == NS_FORM_INPUT_NUMBER) {
@@ -1833,23 +1837,31 @@ HTMLInputElement::SetValue(const nsAStri
       // event, we should keep it that way. Otherwise, we should make sure the
       // element will not fire any event because of the script interaction.
       //
       // NOTE: this is currently quite expensive work (too much string
       // manipulation). We should probably optimize that.
       nsAutoString currentValue;
       GetValue(currentValue);
 
-      SetValueInternal(aValue, false, true);
+      nsresult rv = SetValueInternal(aValue, false, true);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+        return;
+      }
 
       if (mFocusedValue.Equals(currentValue)) {
         GetValue(mFocusedValue);
       }
     } else {
-      SetValueInternal(aValue, false, true);
+      nsresult rv = SetValueInternal(aValue, false, true);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+        return;
+      }
     }
   }
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetValue(const nsAString& aValue)
 {
   ErrorResult rv;
@@ -2408,17 +2420,18 @@ HTMLInputElement::SetUserInput(const nsA
 
   if (mType == NS_FORM_INPUT_FILE)
   {
     Sequence<nsString> list;
     list.AppendElement(aValue);
     MozSetFileNameArray(list);
     return NS_OK;
   } else {
-    SetValueInternal(aValue, true, true);
+    nsresult rv = SetValueInternal(aValue, true, true);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        true);
 
   // If this element is not currently focused, it won't receive a change event for this
@@ -2834,17 +2847,19 @@ HTMLInputElement::SetValueInternal(const
       }
       // else DoneCreatingElement calls us again once mParserCreating is false
 
       if (aSetValueChanged) {
         SetValueChanged(true);
       }
 
       if (IsSingleLineTextControl(false)) {
-        mInputData.mState->SetValue(value, aUserInput, aSetValueChanged);
+        if (!mInputData.mState->SetValue(value, aUserInput, aSetValueChanged)) {
+          return NS_ERROR_OUT_OF_MEMORY;
+        }
         if (mType == NS_FORM_INPUT_EMAIL) {
           UpdateAllValidityStates(mParserCreating);
         }
       } else {
         nsMemory::Free(mInputData.mValue);
         mInputData.mValue = ToNewUnicode(value);
         if (aSetValueChanged) {
           SetValueChanged(true);
@@ -3429,17 +3444,18 @@ HTMLInputElement::PreHandleEvent(EventCh
   // Fire onchange (if necessary), before we do the blur, bug 357684.
   if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     // Experimental mobile types rely on the system UI to prevent users to not
     // set invalid values but we have to be extra-careful. Especially if the
     // option has been enabled on desktop.
     if (IsExperimentalMobileType(mType)) {
       nsAutoString aValue;
       GetValueInternal(aValue);
-      SetValueInternal(aValue, false, false);
+      nsresult rv = SetValueInternal(aValue, false, false);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
     FireChangeEventIfNeeded();
   }
 
   if (mType == NS_FORM_INPUT_RANGE &&
       (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
        aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
     // Just as nsGenericHTMLFormElementWithState::PreHandleEvent calls
@@ -3549,17 +3565,18 @@ HTMLInputElement::PreHandleEvent(EventCh
     }
     if (textControl && aVisitor.mEvent->originalTarget == textControl) {
       if (aVisitor.mEvent->message == NS_EDITOR_INPUT) {
         // Propogate the anon text control's new value to our HTMLInputElement:
         nsAutoString value;
         numberControlFrame->GetValueOfAnonTextControl(value);
         numberControlFrame->HandlingInputEvent(true);
         nsWeakFrame weakNumberControlFrame(numberControlFrame);
-        SetValueInternal(value, true, true);
+        rv = SetValueInternal(value, true, true);
+        NS_ENSURE_SUCCESS(rv, rv);
         if (weakNumberControlFrame.IsAlive()) {
           numberControlFrame->HandlingInputEvent(false);
         }
       }
       else if (aVisitor.mEvent->message == NS_FORM_CHANGE) {
         // We cancel the DOM 'change' event that is fired for any change to our
         // anonymous text control since we fire our own 'change' events and
         // content shouldn't be seeing two 'change' events. Besides that we
@@ -3623,16 +3640,18 @@ HTMLInputElement::CancelRangeThumbDrag(b
   if (aIsForUserEvent) {
     SetValueOfRangeForUserEvent(mRangeThumbDragStartValue);
   } else {
     // Don't dispatch an 'input' event - at least not using
     // DispatchTrustedEvent.
     // TODO: decide what we should do here - bug 851782.
     nsAutoString val;
     ConvertNumberToString(mRangeThumbDragStartValue, val);
+    // TODO: What should we do if SetValueInternal fails?  (The allocation
+    // is small, so we should be fine here.)
     SetValueInternal(val, true, true);
     nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
     if (frame) {
       frame->UpdateForValueChange();
     }
     nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
       new AsyncEventDispatcher(this, NS_LITERAL_STRING("input"), true, false);
     asyncDispatcher->RunDOMEventWhenSafe();
@@ -3641,16 +3660,18 @@ HTMLInputElement::CancelRangeThumbDrag(b
 
 void
 HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
 {
   MOZ_ASSERT(aValue.isFinite());
 
   nsAutoString val;
   ConvertNumberToString(aValue, val);
+  // TODO: What should we do if SetValueInternal fails?  (The allocation
+  // is small, so we should be fine here.)
   SetValueInternal(val, true, true);
   nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   if (frame) {
     frame->UpdateForValueChange();
   }
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
@@ -3732,16 +3753,18 @@ HTMLInputElement::StepNumberControlForUs
   nsresult rv = GetValueIfStepped(aDirection, CALLED_FOR_USER_EVENT, &newValue);
 
   if (NS_FAILED(rv) || !newValue.isFinite()) {
     return; // value should not or will not change
   }
 
   nsAutoString newVal;
   ConvertNumberToString(newValue, newVal);
+  // TODO: What should we do if SetValueInternal fails?  (The allocation
+  // is small, so we should be fine here.)
   SetValueInternal(newVal, true, true);
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        false);
 }
 
@@ -4528,16 +4551,19 @@ HTMLInputElement::HandleTypeChange(uint8
       // SetValueInternal is going to sanitize the value.
       {
         nsAutoString value;
         if (aOldValueMode != VALUE_MODE_VALUE) {
           GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
         } else {
           value = aOldValue;
         }
+        // TODO: What should we do if SetValueInternal fails?  (The allocation
+        // may potentially be big, but most likely we've failed to allocate
+        // before the type change.)
         SetValueInternal(value, false, false);
       }
       break;
     case VALUE_MODE_FILENAME:
     default:
       // We don't care about the value.
       // There is no value sanitizing algorithm for elements in this mode.
       break;
@@ -5211,17 +5237,21 @@ HTMLInputElement::SetRangeText(const nsA
         aSelectionEnd = state->GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    SetValueInternal(value, false, false);
+    nsresult rv = SetValueInternal(value, false, false);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
 
   switch (aSelectMode) {
     case mozilla::dom::SelectionMode::Select:
     {
@@ -5770,16 +5800,19 @@ HTMLInputElement::DoneCreatingElement()
     DoSetChecked(DefaultChecked(), false, true);
     DoSetCheckedChanged(false, false);
   }
 
   // Sanitize the value.
   if (GetValueMode() == VALUE_MODE_VALUE) {
     nsAutoString aValue;
     GetValue(aValue);
+    // TODO: What should we do if SetValueInternal fails?  (The allocation
+    // may potentially be big, but most likely we've failed to allocate
+    // before the type change.)
     SetValueInternal(aValue, false, false);
   }
 
   mShouldInitChecked = false;
 }
 
 EventStates
 HTMLInputElement::IntrinsicState() const
@@ -5924,19 +5957,21 @@ HTMLInputElement::RestoreState(nsPresSta
         break;
       case VALUE_MODE_VALUE:
       case VALUE_MODE_DEFAULT:
         if (GetValueMode() == VALUE_MODE_DEFAULT &&
             mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
 
+        // TODO: What should we do if SetValueInternal fails?  (The allocation
+        // may potentially be big, but most likely we've failed to allocate
+        // before the type change.)
         SetValueInternal(inputState->GetValue(), false, true);
         break;
-        break;
     }
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
   return restoredCheckedState;
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -298,17 +298,19 @@ HTMLTextAreaElement::GetPlaceholderVisib
 nsresult
 HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
                                       bool aUserInput)
 {
   // Need to set the value changed flag here, so that
   // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
   // if needed.
   SetValueChanged(true);
-  mState.SetValue(aValue, aUserInput, true);
+  if (!mState.SetValue(aValue, aUserInput, true)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetValue(const nsAString& aValue)
 {
   // If the value has been set by a script, we basically want to keep the
@@ -316,33 +318,33 @@ HTMLTextAreaElement::SetValue(const nsAS
   // event, we should keep it that way. Otherwise, we should make sure the
   // element will not fire any event because of the script interaction.
   //
   // NOTE: this is currently quite expensive work (too much string
   // manipulation). We should probably optimize that.
   nsAutoString currentValue;
   GetValueInternal(currentValue, true);
 
-  SetValueInternal(aValue, false);
+  nsresult rv = SetValueInternal(aValue, false);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   if (mFocusedValue.Equals(currentValue)) {
     GetValueInternal(mFocusedValue, true);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-  SetValueInternal(aValue, true);
-  return NS_OK;
+  return SetValueInternal(aValue, true);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
 {
   bool previousValue = mValueChanged;
 
   mValueChanged = aValueChanged;
@@ -961,17 +963,21 @@ HTMLTextAreaElement::SetRangeText(const 
         aSelectionEnd = mState.GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    SetValueInternal(value, false);
+    nsresult rv = SetValueInternal(value, false);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
 
   switch (aSelectMode) {
     case mozilla::dom::SelectionMode::Select:
     {
@@ -1014,17 +1020,18 @@ HTMLTextAreaElement::SetRangeText(const 
 
 nsresult
 HTMLTextAreaElement::Reset()
 {
   nsresult rv;
 
   // To get the initial spellchecking, reset value to
   // empty string before setting the default value.
-  SetValue(EmptyString());
+  rv = SetValue(EmptyString());
+  NS_ENSURE_SUCCESS(rv, rv);
   nsAutoString resetVal;
   GetDefaultValue(resetVal);
   rv = SetValue(resetVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetValueChanged(false);
   return NS_OK;
 }
@@ -1105,17 +1112,18 @@ bool
 HTMLTextAreaElement::RestoreState(nsPresState* aState)
 {
   nsCOMPtr<nsISupportsString> state
     (do_QueryInterface(aState->GetStateProperty()));
   
   if (state) {
     nsAutoString data;
     state->GetData(data);
-    SetValue(data);
+    nsresult rv = SetValue(data);
+    NS_ENSURE_SUCCESS(rv, false);
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
   return false;
 }
@@ -1289,17 +1297,17 @@ nsresult
 HTMLTextAreaElement::CopyInnerTo(Element* aDest)
 {
   nsresult rv = nsGenericHTMLFormElementWithState::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     nsAutoString value;
     GetValueInternal(value, true);
-    static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
+    return static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
   }
   return NS_OK;
 }
 
 bool
 HTMLTextAreaElement::IsMutable() const
 {
   return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1392,17 +1392,18 @@ nsTextEditorState::PrepareEditor(const n
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
 
     rv = newEditor->EnableUndo(false);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    SetValue(defaultValue, false, false);
+    bool success = SetValue(defaultValue, false, false);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     rv = newEditor->EnableUndo(true);
     NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
 
     // Now restore the original editor flags.
     rv = newEditor->SetFlags(editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1650,17 +1651,19 @@ nsTextEditorState::UnbindFromFrame(nsTex
     mTextListener = nullptr;
   }
 
   mBoundFrame = nullptr;
 
   // Now that we don't have a frame any more, store the value in the text buffer.
   // The only case where we don't do this is if a value transfer is in progress.
   if (!mValueTransferInProgress) {
-    SetValue(value, false, false);
+    bool success = SetValue(value, false, false);
+    // TODO Find something better to do if this fails...
+    NS_ENSURE_TRUE_VOID(success);
   }
 
   if (mRootNode && mMutationObserver) {
     mRootNode->RemoveMutationObserver(mMutationObserver);
     mMutationObserver = nullptr;
   }
 
   // Unbind the anonymous content from the tree.
@@ -1863,20 +1866,22 @@ nsTextEditorState::GetValue(nsAString& a
     if (!mTextCtrlElement->ValueChanged() || !mValue) {
       mTextCtrlElement->GetDefaultValueFromContent(aValue);
     } else {
       aValue = NS_ConvertUTF8toUTF16(*mValue);
     }
   }
 }
 
-void
+bool
 nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
                             bool aSetValueChanged)
 {
+  mozilla::fallible_t fallible;
+
   if (mEditor && mBoundFrame) {
     // The InsertText call below might flush pending notifications, which
     // could lead into a scheduled PrepareEditor to be called.  That will
     // lead to crashes (or worse) because we'd be initializing the editor
     // before InsertText returns.  This script blocker makes sure that
     // PrepareEditor cannot be called prematurely.
     nsAutoScriptBlocker scriptBlocker;
 
@@ -1896,26 +1901,31 @@ nsTextEditorState::SetValue(const nsAStr
     if (!currentValue.Equals(aValue))
     {
       ValueSetter valueSetter(mEditor);
 
       // \r is an illegal character in the dom, but people use them,
       // so convert windows and mac platform linebreaks to \n:
       // Unfortunately aValue is declared const, so we have to copy
       // in order to do this substitution.
-      nsString newValue(aValue);
+      nsString newValue;
+      if (!newValue.Assign(aValue, fallible)) {
+        return false;
+      }
       if (aValue.FindChar(char16_t('\r')) != -1) {
-        nsContentUtils::PlatformToDOMLineBreaks(newValue);
+        if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
+          return false;
+        }
       }
 
       nsCOMPtr<nsIDOMDocument> domDoc;
       mEditor->GetDocument(getter_AddRefs(domDoc));
       if (!domDoc) {
         NS_WARNING("Why don't we have a document?");
-        return;
+        return true;
       }
 
       // Time to mess with our security context... See comments in GetValue()
       // for why this is needed.  Note that we have to do this up here, because
       // otherwise SelectAll() will fail.
       {
         AutoNoJSAPI nojsapi;
 
@@ -1942,17 +1952,17 @@ nsTextEditorState::SetValue(const nsAStr
           // Collapse selection to the end so that we can append data.
           mBoundFrame->SelectAllOrCollapseToEndOfText(false);
         }
         const nsAString& insertValue =
           StringTail(newValue, newlength - currentLength);
         nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(mEditor);
         if (!plaintextEditor || !weakFrame.IsAlive()) {
           NS_WARNING("Somehow not a plaintext editor?");
-          return;
+          return true;
         }
 
         valueSetter.Init();
 
         // get the flags, remove readonly and disabled, set the value,
         // restore flags
         uint32_t flags, savedFlags;
         mEditor->GetFlags(&savedFlags);
@@ -1981,50 +1991,61 @@ nsTextEditorState::SetValue(const nsAStr
 
         if (!weakFrame.IsAlive()) {
           // If the frame was destroyed because of a flush somewhere inside
           // InsertText, mBoundFrame here will be false.  But it's also possible
           // for the frame to go away because of another reason (such as deleting
           // the existing selection -- see bug 574558), in which case we don't
           // need to reset the value here.
           if (!mBoundFrame) {
-            SetValue(newValue, false, aSetValueChanged);
+            return SetValue(newValue, false, aSetValueChanged);
           }
-          return;
+          return true;
         }
 
         if (!IsSingleLineTextControl()) {
-          mCachedValue = newValue;
+          if (!mCachedValue.Assign(newValue, fallible)) {
+            return false;
+          }
         }
 
         plaintextEditor->SetMaxTextLength(savedMaxLength);
         mEditor->SetFlags(savedFlags);
         if (selPriv)
           selPriv->EndBatchChanges();
       }
     }
   } else {
     if (!mValue) {
       mValue = new nsCString;
     }
-    nsString value(aValue);
-    nsContentUtils::PlatformToDOMLineBreaks(value);
-    CopyUTF16toUTF8(value, *mValue);
+    nsString value;
+    if (!value.Assign(aValue, fallible)) {
+      return false;
+    }
+    if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
+      return false;
+    }
+    if (!CopyUTF16toUTF8(value, *mValue, fallible)) {
+      return false;
+    }
 
     // Update the frame display if needed
     if (mBoundFrame) {
       mBoundFrame->UpdateValueDisplay(true);
     }
   }
 
   // If we've reached the point where the root node has been created, we
   // can assume that it's safe to notify.
   ValueWasChanged(!!mRootNode);
 
   mTextCtrlElement->OnValueChanged(!!mRootNode);
+
+  return true;
 }
 
 void
 nsTextEditorState::InitializeKeyboardEventListeners()
 {
   //register key listeners
   nsCOMPtr<EventTarget> target = do_QueryInterface(mTextCtrlElement);
   EventListenerManager* manager = target->GetOrCreateListenerManager();
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -137,18 +137,19 @@ public:
   nsIEditor* GetEditor();
   nsISelectionController* GetSelectionController() const;
   nsFrameSelection* GetConstFrameSelection();
   nsresult BindToFrame(nsTextControlFrame* aFrame);
   void UnbindFromFrame(nsTextControlFrame* aFrame);
   nsresult PrepareEditor(const nsAString *aValue = nullptr);
   void InitializeKeyboardEventListeners();
 
-  void SetValue(const nsAString& aValue, bool aUserInput,
-                bool aSetValueAsChanged);
+  MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue,
+                                       bool aUserInput,
+                                       bool aSetValueAsChanged);
   void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
   void EmptyValue() { if (mValue) mValue->Truncate(); }
   bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
 
   nsresult CreatePlaceholderNode();
 
   mozilla::dom::Element* GetRootNode() {
     if (!mRootNode)
--- a/dom/media/android/AndroidMediaResourceServer.cpp
+++ b/dom/media/android/AndroidMediaResourceServer.cpp
@@ -15,18 +15,20 @@
 #include "nsReadLine.h"
 #include "nsNetCID.h"
 #include "VideoUtils.h"
 #include "MediaResource.h"
 #include "AndroidMediaResourceServer.h"
 
 #if defined(_MSC_VER)
 #define strtoll _strtoi64
+#if _MSC_VER < 1900
 #define snprintf _snprintf_s
 #endif
+#endif
 
 using namespace mozilla;
 
 /*
   ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals
   with the carriage return/line feed requirements of HTTP requests.
 */
 template<typename CharT, class StreamType, class StringType>
--- a/dom/media/eme/CDMCallbackProxy.cpp
+++ b/dom/media/eme/CDMCallbackProxy.cpp
@@ -136,49 +136,49 @@ CDMCallbackProxy::RejectPromise(uint32_t
                                aMessage);
   NS_DispatchToMainThread(task);
 }
 
 class SessionMessageTask : public nsRunnable {
 public:
   SessionMessageTask(CDMProxy* aProxy,
                      const nsCString& aSessionId,
-                     const nsTArray<uint8_t>& aMessage,
-                     const nsCString& aDestinationURL)
+                     GMPSessionMessageType aMessageType,
+                     const nsTArray<uint8_t>& aMessage)
     : mProxy(aProxy)
     , mSid(NS_ConvertUTF8toUTF16(aSessionId))
-    , mURL(NS_ConvertUTF8toUTF16(aDestinationURL))
+    , mMsgType(aMessageType)
   {
     mMsg.AppendElements(aMessage);
   }
 
   NS_IMETHOD Run() {
-    mProxy->OnSessionMessage(mSid, mMsg, mURL);
+    mProxy->OnSessionMessage(mSid, mMsgType, mMsg);
     return NS_OK;
   }
 
   nsRefPtr<CDMProxy> mProxy;
   dom::PromiseId mPid;
   nsString mSid;
+  GMPSessionMessageType mMsgType;
   nsTArray<uint8_t> mMsg;
-  nsString mURL;
 };
 
 void
 CDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
-                                 const nsTArray<uint8_t>& aMessage,
-                                 const nsCString& aDestinationURL)
+                                 GMPSessionMessageType aMessageType,
+                                 const nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(mProxy->IsOnGMPThread());
 
   nsRefPtr<nsIRunnable> task;
   task = new SessionMessageTask(mProxy,
                                 aSessionId,
-                                aMessage,
-                                aDestinationURL);
+                                aMessageType,
+                                aMessage);
   NS_DispatchToMainThread(task);
 }
 
 class ExpirationChangeTask : public nsRunnable {
 public:
   ExpirationChangeTask(CDMProxy* aProxy,
                        const nsCString& aSessionId,
                        GMPTimestamp aExpiryTime)
--- a/dom/media/eme/CDMCallbackProxy.h
+++ b/dom/media/eme/CDMCallbackProxy.h
@@ -25,18 +25,18 @@ public:
 
   virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aException,
                              const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual void SessionMessage(const nsCString& aSessionId,
-                              const nsTArray<uint8_t>& aMessage,
-                              const nsCString& aDestinationURL) MOZ_OVERRIDE;
+                              GMPSessionMessageType aMessageType,
+                              const nsTArray<uint8_t>& aMessage) MOZ_OVERRIDE;
 
   virtual void ExpirationChange(const nsCString& aSessionId,
                                 GMPTimestamp aExpiryTime) MOZ_OVERRIDE;
 
   virtual void SessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual void SessionError(const nsCString& aSessionId,
                             nsresult aException,
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -404,28 +404,38 @@ CDMProxy::OnResolveLoadSessionPromise(ui
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
+static dom::MediaKeyMessageType
+ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
+  switch (aMessageType) {
+    case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
+    case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
+    case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
+    default: return dom::MediaKeyMessageType::License_request;
+  };
+};
+
 void
 CDMProxy::OnSessionMessage(const nsAString& aSessionId,
-                           nsTArray<uint8_t>& aMessage,
-                           const nsAString& aDestinationURL)
+                           GMPSessionMessageType aMessageType,
+                           nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
-    session->DispatchKeyMessage(aMessage, aDestinationURL);
+    session->DispatchKeyMessage(ToMediaKeyMessageType(aMessageType), aMessage);
   }
 }
 
 void
 CDMProxy::OnKeysChange(const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -115,18 +115,18 @@ public:
   void OnSetSessionId(uint32_t aCreateSessionToken,
                       const nsAString& aSessionId);
 
   // Main thread only.
   void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess);
 
   // Main thread only.
   void OnSessionMessage(const nsAString& aSessionId,
-                        nsTArray<uint8_t>& aMessage,
-                        const nsAString& aDestinationURL);
+                        GMPSessionMessageType aMessageType,
+                        nsTArray<uint8_t>& aMessage);
 
   // Main thread only.
   void OnExpirationChange(const nsAString& aSessionId,
                           GMPTimestamp aExpiryTime);
 
   // Main thread only.
   void OnSessionClosed(const nsAString& aSessionId);
 
--- a/dom/media/eme/MediaKeyMessageEvent.cpp
+++ b/dom/media/eme/MediaKeyMessageEvent.cpp
@@ -58,23 +58,23 @@ MediaKeyMessageEvent::AsMediaKeyMessageE
 JSObject*
 MediaKeyMessageEvent::WrapObjectInternal(JSContext* aCx)
 {
   return MediaKeyMessageEventBinding::Wrap(aCx, this);
 }
 
 already_AddRefed<MediaKeyMessageEvent>
 MediaKeyMessageEvent::Constructor(EventTarget* aOwner,
-                                  const nsAString& aURL,
+                                  MediaKeyMessageType aMessageType,
                                   const nsTArray<uint8_t>& aMessage)
 {
   nsRefPtr<MediaKeyMessageEvent> e = new MediaKeyMessageEvent(aOwner);
   e->InitEvent(NS_LITERAL_STRING("message"), false, false);
+  e->mMessageType = aMessageType;
   e->mRawMessage = aMessage;
-  e->mDestinationURL = aURL;
   e->SetTrusted(true);
   return e.forget();
 }
 
 already_AddRefed<MediaKeyMessageEvent>
 MediaKeyMessageEvent::Constructor(const GlobalObject& aGlobal,
                                   const nsAString& aType,
                                   const MediaKeyMessageEventInit& aEventInitDict,
@@ -92,17 +92,17 @@ MediaKeyMessageEvent::Constructor(const 
     data = a.Data();
     length = a.Length();
   }
   e->mMessage = ArrayBuffer::Create(aGlobal.Context(), length, data);
   if (!e->mMessage) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
-  e->mDestinationURL = aEventInitDict.mDestinationURL;
+  e->mMessageType = aEventInitDict.mMessageType;
   e->SetTrusted(trusted);
   return e.forget();
 }
 
 void
 MediaKeyMessageEvent::GetMessage(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aMessage,
                                  ErrorResult& aRv)
@@ -116,16 +116,10 @@ MediaKeyMessageEvent::GetMessage(JSConte
       return;
     }
     mRawMessage.Clear();
   }
   JS::ExposeObjectToActiveJS(mMessage);
   aMessage.set(mMessage);
 }
 
-void
-MediaKeyMessageEvent::GetDestinationURL(nsString& aRetVal) const
-{
-  aRetVal = mDestinationURL;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeyMessageEvent.h
+++ b/dom/media/eme/MediaKeyMessageEvent.h
@@ -26,41 +26,41 @@ class MediaKeyMessageEvent MOZ_FINAL : p
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaKeyMessageEvent, Event)
 protected:
   virtual ~MediaKeyMessageEvent();
   explicit MediaKeyMessageEvent(EventTarget* aOwner);
 
+  MediaKeyMessageType mMessageType;
   JS::Heap<JSObject*> mMessage;
-  nsString mDestinationURL;
 
 public:
   virtual MediaKeyMessageEvent* AsMediaKeyMessageEvent();
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE;
 
   static already_AddRefed<MediaKeyMessageEvent>
-    Constructor(EventTarget* aOwner,
-                const nsAString& aURL,
-                const nsTArray<uint8_t>& aMessage);
+  Constructor(EventTarget* aOwner,
+              MediaKeyMessageType aMessageType,
+              const nsTArray<uint8_t>& aMessage);
 
   static already_AddRefed<MediaKeyMessageEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const MediaKeyMessageEventInit& aEventInitDict,
               ErrorResult& aRv);
 
+  MediaKeyMessageType MessageType() const { return mMessageType; }
+
   void GetMessage(JSContext* cx,
                   JS::MutableHandle<JSObject*> aMessage,
                   ErrorResult& aRv);
 
-  void GetDestinationURL(nsString& aRetVal) const;
-
 private:
   nsTArray<uint8_t> mRawMessage;
 };
 
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -272,21 +272,21 @@ MediaKeySession::GetUsableKeyIds(ErrorRe
     array.AppendElement(keyIds[i]);
   }
   promise->MaybeResolve(array);
 
   return promise.forget();
 }
 
 void
-MediaKeySession::DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
-                                    const nsAString& aURL)
+MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
+                                    const nsTArray<uint8_t>& aMessage)
 {
   nsRefPtr<MediaKeyMessageEvent> event(
-    MediaKeyMessageEvent::Constructor(this, aURL, aMessage));
+    MediaKeyMessageEvent::Constructor(this, aMessageType, aMessage));
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, event);
   asyncDispatcher->PostDOMEvent();
 }
 
 void
 MediaKeySession::DispatchKeyError(uint32_t aSystemCode)
 {
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -13,16 +13,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsCOMPtr.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeySessionBinding.h"
 #include "mozilla/dom/MediaKeysBinding.h"
+#include "mozilla/dom/MediaKeyMessageEventBinding.h"
 
 struct JSContext;
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
@@ -74,18 +75,18 @@ public:
                                    ErrorResult& aRv);
 
   already_AddRefed<Promise> Close(ErrorResult& aRv);
 
   already_AddRefed<Promise> Remove(ErrorResult& aRv);
 
   already_AddRefed<Promise> GetUsableKeyIds(ErrorResult& aRv);
 
-  void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
-                          const nsAString& aURL);
+  void DispatchKeyMessage(MediaKeyMessageType aMessageType,
+                          const nsTArray<uint8_t>& aMessage);
 
   void DispatchKeyError(uint32_t system_code);
 
   void DispatchKeysChange();
 
   void OnClosed();
 
   bool IsClosed() const;
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -520,16 +520,24 @@ MP4Reader::GetDecoderData(TrackType aTra
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
                             int64_t aTimeThreshold)
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
 
+  if (mShutdown) {
+    NS_WARNING("RequestVideoData on shutdown MP4Reader!");
+    MonitorAutoLock lock(mVideo.mMonitor);
+    nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
+    p->Reject(CANCELED, __func__);
+    return p;
+  }
+
   MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
 
   bool eos = false;
   if (aSkipToNextKeyframe) {
     uint32_t parsed = 0;
     eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
     if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) {
       NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe.");
@@ -550,17 +558,22 @@ MP4Reader::RequestVideoData(bool aSkipTo
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MP4Reader::RequestAudioData()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestAudioData");
   MonitorAutoLock lock(mAudio.mMonitor);
   nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
-  ScheduleUpdate(kAudio);
+  if (!mShutdown) {
+    ScheduleUpdate(kAudio);
+  } else {
+    NS_WARNING("RequestAudioData on shutdown MP4Reader!");
+    p->Reject(CANCELED, __func__);
+  }
   return p;
 }
 
 void
 MP4Reader::ScheduleUpdate(TrackType aTrack)
 {
   auto& decoder = GetDecoderData(aTrack);
   decoder.mMonitor.AssertCurrentThreadOwns();
@@ -735,22 +748,26 @@ MP4Reader::SizeOfQueue(TrackType aTrack)
 
 nsresult
 MP4Reader::ResetDecode()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   Flush(kVideo);
   {
     MonitorAutoLock mon(mDemuxerMonitor);
-    mDemuxer->SeekVideo(0);
+    if (mDemuxer) {
+      mDemuxer->SeekVideo(0);
+    }
   }
   Flush(kAudio);
   {
     MonitorAutoLock mon(mDemuxerMonitor);
-    mDemuxer->SeekAudio(0);
+    if (mDemuxer) {
+      mDemuxer->SeekAudio(0);
+    }
   }
   return MediaDecoderReader::ResetDecode();
 }
 
 void
 MP4Reader::Output(TrackType aTrack, MediaData* aSample)
 {
 #ifdef LOG_SAMPLE_DECODE
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v2[fake]
+APIs: encode-video[h264], decode-video[h264], eme-decrypt-v3[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -111,18 +111,18 @@ void FakeDecryptor::DecryptingComplete()
 }
 
 void
 FakeDecryptor::Message(const std::string& aMessage)
 {
   MOZ_ASSERT(sInstance);
   const static std::string sid("fake-session-id");
   sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
-                                       (const uint8_t*)aMessage.c_str(), aMessage.size(),
-                                       nullptr, 0);
+                                       kGMPLicenseRequest,
+                                       (const uint8_t*)aMessage.c_str(), aMessage.size());
 }
 
 std::vector<std::string>
 Tokenize(const std::string& aString)
 {
   std::stringstream strstr(aString);
   std::istream_iterator<std::string> it(strstr), end;
   return std::vector<std::string>(it, end);
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -78,26 +78,25 @@ GMPDecryptorChild::RejectPromise(uint32_
 {
   CALL_ON_GMP_THREAD(SendRejectPromise,
                      aPromiseId, aException, nsAutoCString(aMessage, aMessageLength));
 }
 
 void
 GMPDecryptorChild::SessionMessage(const char* aSessionId,
                                   uint32_t aSessionIdLength,
+                                  GMPSessionMessageType aMessageType,
                                   const uint8_t* aMessage,
-                                  uint32_t aMessageLength,
-                                  const char* aDestinationURL,
-                                  uint32_t aDestinationURLLength)
+                                  uint32_t aMessageLength)
 {
   nsTArray<uint8_t> msg;
   msg.AppendElements(aMessage, aMessageLength);
   CALL_ON_GMP_THREAD(SendSessionMessage,
-                     nsAutoCString(aSessionId, aSessionIdLength), msg,
-                     nsAutoCString(aDestinationURL, aDestinationURLLength));
+                     nsAutoCString(aSessionId, aSessionIdLength),
+                     aMessageType, msg);
 }
 
 void
 GMPDecryptorChild::ExpirationChange(const char* aSessionId,
                                     uint32_t aSessionIdLength,
                                     GMPTimestamp aExpiryTime)
 {
   CALL_ON_GMP_THREAD(SendExpirationChange,
--- a/dom/media/gmp/GMPDecryptorChild.h
+++ b/dom/media/gmp/GMPDecryptorChild.h
@@ -40,20 +40,19 @@ public:
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              GMPDOMException aException,
                              const char* aMessage,
                              uint32_t aMessageLength) MOZ_OVERRIDE;
 
   virtual void SessionMessage(const char* aSessionId,
                               uint32_t aSessionIdLength,
+                              GMPSessionMessageType aMessageType,
                               const uint8_t* aMessage,
-                              uint32_t aMessageLength,
-                              const char* aDestinationURL,
-                              uint32_t aDestinationURLLength) MOZ_OVERRIDE;
+                              uint32_t aMessageLength) MOZ_OVERRIDE;
 
   virtual void ExpirationChange(const char* aSessionId,
                                  uint32_t aSessionIdLength,
                                  GMPTimestamp aExpiryTime) MOZ_OVERRIDE;
 
    virtual void SessionClosed(const char* aSessionId,
                              uint32_t aSessionIdLength) MOZ_OVERRIDE;
 
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -208,24 +208,24 @@ GMPDecryptorParent::RecvRejectPromise(co
     return false;
   }
   mCallback->RejectPromise(aPromiseId, GMPExToNsresult(aException), aMessage);
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId,
-                                       const nsTArray<uint8_t>& aMessage,
-                                       const nsCString& aDestinationURL)
+                                       const GMPSessionMessageType& aMessageType,
+                                       const nsTArray<uint8_t>& aMessage)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
     return false;
   }
-  mCallback->SessionMessage(aSessionId, aMessage, aDestinationURL);
+  mCallback->SessionMessage(aSessionId, aMessageType, aMessage);
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
                                          const double& aExpiryTime)
 {
   if (!mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -73,18 +73,18 @@ private:
 
   virtual bool RecvResolvePromise(const uint32_t& aPromiseId) MOZ_OVERRIDE;
 
   virtual bool RecvRejectPromise(const uint32_t& aPromiseId,
                                  const GMPDOMException& aException,
                                  const nsCString& aMessage) MOZ_OVERRIDE;
 
   virtual bool RecvSessionMessage(const nsCString& aSessionId,
-                                  const nsTArray<uint8_t>& aMessage,
-                                  const nsCString& aDestinationURL) MOZ_OVERRIDE;
+                                  const GMPSessionMessageType& aMessageType,
+                                  const nsTArray<uint8_t>& aMessage) MOZ_OVERRIDE;
 
   virtual bool RecvExpirationChange(const nsCString& aSessionId,
                                     const double& aExpiryTime) MOZ_OVERRIDE;
 
   virtual bool RecvSessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual bool RecvSessionError(const nsCString& aSessionId,
                                 const GMPDOMException& aException,
--- a/dom/media/gmp/GMPDecryptorProxy.h
+++ b/dom/media/gmp/GMPDecryptorProxy.h
@@ -26,18 +26,18 @@ public:
 
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aException,
                              const nsCString& aSessionId) = 0;
 
   virtual void SessionMessage(const nsCString& aSessionId,
-                              const nsTArray<uint8_t>& aMessage,
-                              const nsCString& aDestinationURL) = 0;
+                              GMPSessionMessageType aMessageType,
+                              const nsTArray<uint8_t>& aMessage) = 0;
 
   virtual void ExpirationChange(const nsCString& aSessionId,
                                 GMPTimestamp aExpiryTime) = 0;
 
   virtual void SessionClosed(const nsCString& aSessionId) = 0;
 
   virtual void SessionError(const nsCString& aSessionId,
                             nsresult aException,
--- a/dom/media/gmp/GMPMessageUtils.h
+++ b/dom/media/gmp/GMPMessageUtils.h
@@ -49,16 +49,23 @@ struct ParamTraits<GMPVideoFrameType>
 {};
 
 template<>
 struct ParamTraits<GMPDOMException>
 : public EnumSerializer<GMPDOMException, GMPDomExceptionValidator>
 {};
 
 template <>
+struct ParamTraits<GMPSessionMessageType>
+: public ContiguousEnumSerializer<GMPSessionMessageType,
+                                  kGMPLicenseRequest,
+                                  kGMPMessageInvalid>
+{};
+
+template <>
 struct ParamTraits<GMPSessionType>
 : public ContiguousEnumSerializer<GMPSessionType,
                                   kGMPTemporySession,
                                   kGMPSessionInvalid>
 {};
 
 template <>
 struct ParamTraits<GMPAudioCodecType>
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PGMP;
 include GMPTypes;
 
+using GMPSessionMessageType from  "gmp-decryption.h";
 using GMPSessionType from  "gmp-decryption.h";
 using GMPDOMException from "gmp-decryption.h";
 using GMPErr from "gmp-errors.h";
 
 namespace mozilla {
 namespace gmp {
 
 async protocol PGMPDecryptor
@@ -59,18 +60,18 @@ parent:
 
   ResolvePromise(uint32_t aPromiseId);
 
   RejectPromise(uint32_t aPromiseId,
                 GMPDOMException aDOMExceptionCode,
                 nsCString aMessage);
 
   SessionMessage(nsCString aSessionId,
-                 uint8_t[] aMessage,
-                 nsCString aDestinationURL);
+                 GMPSessionMessageType aMessageType,
+                 uint8_t[] aMessage);
 
   ExpirationChange(nsCString aSessionId, double aExpiryTime);
 
   SessionClosed(nsCString aSessionId);
 
   SessionError(nsCString aSessionId,
                GMPDOMException aDOMExceptionCode,
                uint32_t aSystemCode,
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -63,16 +63,23 @@ enum GMPDOMException {
   kGMPInvalidModificationError = 13,
   kGMPInvalidAccessError = 15,
   kGMPSecurityError = 18,
   kGMPAbortError = 20,
   kGMPQuotaExceededError = 22,
   kGMPTimeoutError = 23
 };
 
+enum GMPSessionMessageType {
+  kGMPLicenseRequest = 0,
+  kGMPLicenseRenewal = 1,
+  kGMPLicenseRelease = 2,
+  kGMPMessageInvalid = 3 // Must always be last.
+};
+
 // Time in milliseconds, as offset from epoch, 1 Jan 1970.
 typedef int64_t GMPTimestamp;
 
 // Capability definitions. The capabilities of the EME GMP are reported
 // to Gecko by calling the GMPDecryptorCallback::SetCapabilities()
 // callback and specifying the logical OR of the GMP_EME_CAP_* flags below.
 //
 // Note the DECRYPT and the DECRYPT_AND_DECODE are mutually exclusive;
@@ -134,20 +141,19 @@ public:
                              const char* aMessage,
                              uint32_t aMessageLength) = 0;
 
   // Called by the CDM when it has a message for a session.
   // Length parameters should not include null termination.
   // aSessionId must be null terminated.
   virtual void SessionMessage(const char* aSessionId,
                               uint32_t aSessionIdLength,
+                              GMPSessionMessageType aMessageType,
                               const uint8_t* aMessage,
-                              uint32_t aMessageLength,
-                              const char* aDestinationURL,
-                              uint32_t aDestinationURLLength) = 0;
+                              uint32_t aMessageLength) = 0;
 
   // aSessionId must be null terminated.
    virtual void ExpirationChange(const char* aSessionId,
                                  uint32_t aSessionIdLength,
                                  GMPTimestamp aExpiryTime) = 0;
 
   // Called by the GMP when a session is closed. All file IO
   // that a session requires should be complete before calling this.
@@ -208,17 +214,17 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
-#define GMP_API_DECRYPTOR "eme-decrypt-v2"
+#define GMP_API_DECRYPTOR "eme-decrypt-v3"
 
 // API exposed by plugin library to manage decryption sessions.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
 public:
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -1078,18 +1078,18 @@ class GMPStorageTest : public GMPDecrypt
 
   void SetFinished() {
     mFinished = true;
     Shutdown();
     NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GMPStorageTest::Dummy));
   }
 
   virtual void SessionMessage(const nsCString& aSessionId,
-                              const nsTArray<uint8_t>& aMessage,
-                              const nsCString& aDestinationURL) MOZ_OVERRIDE
+                              GMPSessionMessageType aMessageType,
+                              const nsTArray<uint8_t>& aMessage) MOZ_OVERRIDE
   {
     MonitorAutoLock mon(mMonitor);
 
     nsCString msg((const char*)aMessage.Elements(), aMessage.Length());
     EXPECT_TRUE(mExpected.Length() > 0);
     bool matches = mExpected[0].mMessage.Equals(msg);
     EXPECT_STREQ(mExpected[0].mMessage.get(), msg.get());
     if (mExpected.Length() > 0 && matches) {
--- a/dom/nfc/NfcContentHelper.js
+++ b/dom/nfc/NfcContentHelper.js
@@ -380,17 +380,17 @@ NfcContentHelper.prototype = {
     }
     delete this._requestMap[requestId];
 
     if (result.errorMsg) {
       callback.notifyError(result.errorMsg);
       return;
     }
 
-    let ndefMsg = [];
+    let ndefMsg = new this._window.Array();
     let records = result.records;
     for (let i = 0; i < records.length; i++) {
       let record = records[i];
       ndefMsg.push(new this._window.MozNDEFRecord({tnf: record.tnf,
                                                    type: record.type,
                                                    id: record.id,
                                                    payload: record.payload}));
     }
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -403,17 +403,17 @@ static bool
 CreateNPObjWrapperTable()
 {
   MOZ_ASSERT(!sNPObjWrappers.ops);
 
   if (!RegisterGCCallbacks()) {
     return false;
   }
 
-  PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nullptr,
+  PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(),
                     sizeof(NPObjWrapperHashEntry));
   return true;
 }
 
 static void
 DestroyNPObjWrapperTable()
 {
   MOZ_ASSERT(sNPObjWrappers.EntryCount() == 0);
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
 support-files =
   test_headers_common.js
   test_headers_mainthread.js
   worker_test_fetch_basic.js
   worker_test_fetch_basic_http.js
+  worker_test_fetch_cors.js
   worker_wrapper.js
 
 [test_headers.html]
 [test_fetch_basic.html]
 [test_fetch_basic_http.html]
+[test_fetch_cors.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.html
@@ -0,0 +1,57 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1039846 - Test fetch() CORS mode</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var worker;
+function testOnWorker(done) {
+  ok(true, "=== Start Worker Tests ===");
+  worker = new Worker("worker_test_fetch_cors.js");
+  worker.onmessage = function(event) {
+    if (event.data.type == "finish") {
+      ok(true, "=== Finish Worker Tests ===");
+      done();
+    } else if (event.data.type == "status") {
+      ok(event.data.status, event.data.msg);
+    }
+  }
+
+  worker.onerror = function(event) {
+    ok(false, "Worker had an error: " + event.message);
+    ok(true, "=== Finish Worker Tests ===");
+    done();
+  };
+
+  worker.postMessage("start");
+}
+
+//
+// Driver
+//
+
+SpecialPowers.pushPrefEnv({"set": [
+  ["dom.fetch.enabled", true]
+]}, function() {
+  testOnWorker(function() {
+    SimpleTest.finish();
+  });
+});
+</script>
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
@@ -19,17 +19,21 @@ var passFiles = [['file_XHR_pass1.xml', 
 
 function testURL() {
   var promises = [];
   passFiles.forEach(function(entry) {
     var p = fetch(path + entry[0]).then(function(res) {
       ok(res.type !== "error", "Response should not be an error for " + entry[0]);
       is(res.status, entry[2], "Status should match expected for " + entry[0]);
       is(res.statusText, entry[3], "Status text should match expected for " + entry[0]);
-      ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
+      // This file redirects to pass2
+      if (entry[0] != "file_XHR_pass3.txt")
+        ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
+      else
+        ok(res.url.endsWith(path + "file_XHR_pass2.txt"), "Response url should match request for simple fetch for " + entry[0]);
       is(res.headers.get('content-type'), entry[4], "Response should have content-type for " + entry[0]);
     });
     promises.push(p);
   });
 
   return Promise.all(promises);
 }
 
@@ -51,17 +55,20 @@ function testURLFail() {
 function testRequestGET() {
   var promises = [];
   passFiles.forEach(function(entry) {
     var req = new Request(path + entry[0], { method: entry[1] });
     var p = fetch(req).then(function(res) {
       ok(res.type !== "error", "Response should not be an error for " + entry[0]);
       is(res.status, entry[2], "Status should match expected for " + entry[0]);
       is(res.statusText, entry[3], "Status text should match expected for " + entry[0]);
-      ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
+      if (entry[0] != "file_XHR_pass3.txt")
+        ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
+      else
+        ok(res.url.endsWith(path + "file_XHR_pass2.txt"), "Response url should match request for simple fetch for " + entry[0]);
       is(res.headers.get('content-type'), entry[4], "Response should have content-type for " + entry[0]);
     });
     promises.push(p);
   });
 
   return Promise.all(promises);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_cors.js
@@ -0,0 +1,1209 @@
+if (typeof ok !== "function") {
+  function ok(a, msg) {
+    postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+  }
+}
+
+if (typeof is !== "function") {
+  function is(a, b, msg) {
+    postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+  }
+}
+
+var path = "/tests/dom/base/test/";
+
+function isNetworkError(response) {
+  return response.type == "error" && response.status === 0 && response.statusText === "";
+}
+
+function isOpaqueResponse(response) {
+  return response.type == "opaque" && response.status === 0 && response.statusText === "";
+}
+
+function testModeSameOrigin() {
+  // Fetch spec Section 4, step 4, "request's mode is same-origin".
+  var req = new Request("http://example.com", { mode: "same-origin" });
+  return fetch(req).then(function(res) {
+    ok(isNetworkError(res), "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
+  });
+}
+
+function testNoCorsCtor() {
+  // Request constructor Step 19.1
+  var simpleMethods = ["GET", "HEAD", "POST"];
+  for (var i = 0; i < simpleMethods.length; ++i) {
+    var r = new Request("http://example.com", { method: simpleMethods[i], mode: "no-cors" });
+    ok(true, "no-cors Request with simple method " + simpleMethods[i] + " is allowed.");
+  }
+
+  var otherMethods = ["DELETE", "OPTIONS", "PUT"];
+  for (var i = 0; i < otherMethods.length; ++i) {
+    try {
+      var r = new Request("http://example.com", { method: otherMethods[i], mode: "no-cors" });
+      ok(false, "no-cors Request with non-simple method " + otherMethods[i] + " is not allowed.");
+    } catch(e) {
+      ok(true, "no-cors Request with non-simple method " + otherMethods[i] + " is not allowed.");
+    }
+  }
+
+  // Request constructor Step 19.2, check guarded headers.
+  var r = new Request(".", { mode: "no-cors" });
+  r.headers.append("Content-Type", "multipart/form-data");
+  is(r.headers.get("content-type"), "multipart/form-data", "Appending simple header should succeed");
+  r.headers.append("custom", "value");
+  ok(!r.headers.has("custom"), "Appending custom header should fail");
+  r.headers.append("DNT", "value");
+  ok(!r.headers.has("DNT"), "Appending forbidden header should fail");
+}
+
+var corsServerPath = "/tests/dom/base/test/file_CrossSiteXHR_server.sjs?";
+function testModeNoCors() {
+  // Fetch spec, section 4, step 4, response tainting should be set opaque, so
+  // that fetching leads to an opaque filtered response in step 8.
+  var r = new Request("http://example.com" + corsServerPath + "status=200&allowOrigin=*", { mode: "no-cors" });
+  return fetch(r).then(function(res) {
+    ok(isOpaqueResponse(res), "no-cors Request fetch should result in opaque response");
+  });
+}
+
+function testModeCors() {
+  var tests = [// Plain request
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+               },
+
+               // undefined username
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: undefined
+               },
+
+               // undefined username and password
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: undefined,
+                 password: undefined
+               },
+
+               // nonempty username
+               { pass: 0,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: "user",
+               },
+
+               // nonempty password
+               // XXXbz this passes for now, because we ignore passwords
+               // without usernames in most cases.
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 password: "password",
+               },
+
+               // Default allowed headers
+               { pass: 1,
+                 method: "GET",
+                 headers: { "Content-Type": "text/plain",
+                            "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE" },
+                 noAllowPreflight: 1,
+               },
+
+               { pass: 0,
+                 method: "GET",
+                 headers: { "Content-Type": "foo/bar",
+                            "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE" },
+                 noAllowPreflight: 1,
+               },
+
+               // Custom headers
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "X-My-Header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
+                 allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my%-header": "myValue" },
+                 allowHeaders: "x-my%-header",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "" },
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "y-my-header",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header y-my-header",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header, y-my-header z",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header, y-my-he(ader",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "myheader": "" },
+                 allowMethods: "myheader",
+               },
+
+               // Multiple custom headers
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue",
+                            "third-header": "thirdValue" },
+                 allowHeaders: "x-my-header, second-header, third-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue",
+                            "third-header": "thirdValue" },
+                 allowHeaders: "x-my-header,second-header,third-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue",
+                            "third-header": "thirdValue" },
+                 allowHeaders: "x-my-header ,second-header ,third-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue",
+                            "third-header": "thirdValue" },
+                 allowHeaders: "x-my-header , second-header , third-header",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue" },
+                 allowHeaders: ",  x-my-header, , ,, second-header, ,   ",
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "second-header": "secondValue" },
+                 allowHeaders: "x-my-header, second-header, unused-header",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "myValue",
+                            "y-my-header": "secondValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "",
+                            "y-my-header": "" },
+                 allowHeaders: "x-my-header",
+               },
+
+               // HEAD requests
+               { pass: 1,
+                 method: "HEAD",
+                 noAllowPreflight: 1,
+               },
+
+               // HEAD with safe headers
+               { pass: 1,
+                 method: "HEAD",
+                 headers: { "Content-Type": "text/plain",
+                            "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE" },
+                 noAllowPreflight: 1,
+               },
+               { pass: 0,
+                 method: "HEAD",
+                 headers: { "Content-Type": "foo/bar",
+                            "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE" },
+                 noAllowPreflight: 1,
+               },
+
+               // HEAD with custom headers
+               { pass: 1,
+                 method: "HEAD",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 0,
+                 method: "HEAD",
+                 headers: { "x-my-header": "myValue" },
+               },
+               { pass: 0,
+                 method: "HEAD",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "",
+               },
+               { pass: 0,
+                 method: "HEAD",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "y-my-header",
+               },
+               { pass: 0,
+                 method: "HEAD",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header y-my-header",
+               },
+
+               // POST tests
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 noAllowPreflight: 1,
+               },
+               { pass: 1,
+                 method: "POST",
+               },
+               { pass: 1,
+                 method: "POST",
+                 noAllowPreflight: 1,
+               },
+
+               // POST with standard headers
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "text/plain" },
+                 noAllowPreflight: 1,
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "multipart/form-data" },
+                 noAllowPreflight: 1,
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
+                 noAllowPreflight: 1,
+               },
+               { pass: 0,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "foo/bar" },
+               },
+               { pass: 0,
+                 method: "POST",
+                 headers: { "Content-Type": "foo/bar" },
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "text/plain",
+                            "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE" },
+                 noAllowPreflight: 1,
+               },
+
+               // POST with custom headers
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Accept": "foo/bar",
+                            "Accept-Language": "sv-SE",
+                            "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "POST",
+                 headers: { "Content-Type": "text/plain",
+                            "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "text/plain",
+                            "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "foo/bar",
+                            "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header, content-type",
+               },
+               { pass: 0,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "foo/bar" },
+                 noAllowPreflight: 1,
+               },
+               { pass: 0,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "foo/bar",
+                            "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "POST",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header",
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "x-my-header": "myValue" },
+                 allowHeaders: "x-my-header, $_%",
+               },
+
+               // Other methods
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "DELETE",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowHeaders: "DELETE",
+               },
+               { pass: 0,
+                 method: "DELETE",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "POST, PUT, DELETE",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "POST, DELETE, PUT",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "DELETE, POST, PUT",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "POST ,PUT ,DELETE",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "POST,PUT,DELETE",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "POST , PUT , DELETE",
+               },
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "  ,,  PUT ,,  ,    , DELETE  ,  ,",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "PUT",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "DELETEZ",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "DELETE PUT",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "DELETE, PUT Z",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "DELETE, PU(T",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "PUT DELETE",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "PUT Z, DELETE",
+               },
+               { pass: 0,
+                 method: "DELETE",
+                 allowMethods: "PU(T, DELETE",
+               },
+               { pass: 0,
+                 method: "PUT",
+                 allowMethods: "put",
+               },
+
+               // Status messages
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 status: 404,
+                 statusMessage: "nothin' here",
+               },
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 status: 401,
+                 statusMessage: "no can do",
+               },
+               { pass: 1,
+                 method: "POST",
+                 body: "hi there",
+                 headers: { "Content-Type": "foo/bar" },
+                 allowHeaders: "content-type",
+                 status: 500,
+                 statusMessage: "server boo",
+               },
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 status: 200,
+                 statusMessage: "Yes!!",
+               },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "header value" },
+                 allowHeaders: "x-my-header",
+                 preflightStatus: 400
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "header value" },
+                 allowHeaders: "x-my-header",
+                 preflightStatus: 200
+               },
+               { pass: 1,
+                 method: "GET",
+                 headers: { "x-my-header": "header value" },
+                 allowHeaders: "x-my-header",
+                 preflightStatus: 204
+               },
+
+               // exposed headers
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "x-my-header",
+                 expectedResponseHeaders: ["x-my-header"],
+               },
+               { pass: 0,
+                 method: "GET",
+                 origin: "http://invalid",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "x-my-header",
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "x-my-header y",
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "y x-my-header",
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "x-my-header, y-my-header z",
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header" },
+                 exposeHeaders: "x-my-header, y-my-hea(er",
+                 expectedResponseHeaders: [],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "x-my-header": "x header",
+                                    "y-my-header": "y header" },
+                 exposeHeaders: "  ,  ,,y-my-header,z-my-header,  ",
+                 expectedResponseHeaders: ["y-my-header"],
+               },
+               { pass: 1,
+                 method: "GET",
+                 responseHeaders: { "Cache-Control": "cacheControl header",
+                                    "Content-Language": "contentLanguage header",
+                                    "Expires":"expires header",
+                                    "Last-Modified":"lastModified header",
+                                    "Pragma":"pragma header",
+                                    "Unexpected":"unexpected header" },
+                 expectedResponseHeaders: ["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"],
+               },
+               // Check that sending a body in the OPTIONS response works
+               { pass: 1,
+                 method: "DELETE",
+                 allowMethods: "DELETE",
+                 preflightBody: "I'm a preflight response body",
+               },
+               ];
+
+  var baseURL = "http://example.org" + corsServerPath;
+  var origin = "http://mochi.test:8888";
+  var fetches = [];
+  for (test of tests) {
+    var req = {
+      url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+      method: test.method,
+      headers: test.headers,
+      uploadProgress: test.uploadProgress,
+      body: test.body,
+      responseHeaders: test.responseHeaders,
+    };
+
+    if (test.pass) {
+       req.url += "&origin=" + escape(origin) +
+                  "&requestMethod=" + test.method;
+    }
+
+    if ("username" in test) {
+      var u = new URL(req.url);
+      u.username = test.username || "";
+      req.url = u.href;
+    }
+
+    if ("password" in test) {
+      var u = new URL(req.url);
+      u.password = test.password || "";
+      req.url = u.href;
+    }
+
+    if (test.noAllowPreflight)
+      req.url += "&noAllowPreflight";
+
+    if (test.pass && "headers" in test) {
+      function isUnsafeHeader(name) {
+        lName = name.toLowerCase();
+        return lName != "accept" &&
+               lName != "accept-language" &&
+               (lName != "content-type" ||
+                ["text/plain",
+                 "multipart/form-data",
+                 "application/x-www-form-urlencoded"]
+                   .indexOf(test.headers[name].toLowerCase()) == -1);
+      }
+      req.url += "&headers=" + escape(test.headers.toSource());
+      reqHeaders =
+        escape([name for (name in test.headers)]
+               .filter(isUnsafeHeader)
+               .map(String.toLowerCase)
+               .sort()
+               .join(","));
+      req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
+    }
+    if ("allowHeaders" in test)
+      req.url += "&allowHeaders=" + escape(test.allowHeaders);
+    if ("allowMethods" in test)
+      req.url += "&allowMethods=" + escape(test.allowMethods);
+    if (test.body)
+      req.url += "&body=" + escape(test.body);
+    if (test.status) {
+      req.url += "&status=" + test.status;
+      req.url += "&statusMessage=" + escape(test.statusMessage);
+    }
+    if (test.preflightStatus)
+      req.url += "&preflightStatus=" + test.preflightStatus;
+    if (test.responseHeaders)
+      req.url += "&responseHeaders=" + escape(test.responseHeaders.toSource());
+    if (test.exposeHeaders)
+      req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
+    if (test.preflightBody)
+      req.url += "&preflightBody=" + escape(test.preflightBody);
+
+    var request = new Request(req.url, { method: req.method, mode: "cors",
+                                         headers: req.headers, body: req.body });
+    fetches.push((function(test, request) {
+      return fetch(request).then(function(res) {
+      dump("Response for " + request.url + "\n");
+        if (test.pass) {
+          ok(!isNetworkError(res),
+            "shouldn't have failed in test for " + test.toSource());
+          if (test.status) {
+            is(res.status, test.status, "wrong status in test for " + test.toSource());
+            is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
+          }
+          else {
+            is(res.status, 200, "wrong status in test for " + test.toSource());
+            is(res.statusText, "OK", "wrong status text for " + test.toSource());
+          }
+          if (test.responseHeaders) {
+            for (header in test.responseHeaders) {
+              if (test.expectedResponseHeaders.indexOf(header) == -1) {
+                is(res.headers.has(header), false,
+                   "|Headers.has()|wrong response header (" + header + ") in test for " +
+                   test.toSource());
+              }
+              else {
+                is(res.headers.get(header), test.responseHeaders[header],
+                   "|Headers.get()|wrong response header (" + header + ") in test for " +
+                   test.toSource());
+              }
+            }
+          }
+
+          return res.text().then(function(v) {
+            if (test.method !== "HEAD") {
+              is(v, "<res>hello pass</res>\n",
+                 "wrong responseText in test for " + test.toSource());
+            }
+            else {
+              is(v, "",
+                 "wrong responseText in HEAD test for " + test.toSource());
+            }
+          });
+        }
+        else {
+          ok(isNetworkError(res),
+            "should have failed in test for " + test.toSource());
+          is(res.status, 0, "wrong status in test for " + test.toSource());
+          is(res.statusText, "", "wrong status text for " + test.toSource());
+          if (test.responseHeaders) {
+            for (header in test.responseHeaders) {
+              is(res.headers.get(header), null,
+                 "wrong response header (" + header + ") in test for " +
+                 test.toSource());
+            }
+          }
+
+          return res.text().then(function(v) {
+            is(v, "",
+               "wrong responseText in test for " + test.toSource());
+          });
+        }
+      });
+    })(test, request));
+  }
+
+  return Promise.all(fetches);
+}
+
+function testCredentials() {
+  var tests = [
+           { pass: 1,
+             method: "GET",
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 0,
+             method: "GET",
+             withCred: 1,
+             allowCred: 0,
+           },
+           { pass: 0,
+             method: "GET",
+             withCred: 1,
+             allowCred: 1,
+             origin: "*",
+           },
+           { pass: 1,
+             method: "GET",
+             withCred: 0,
+             allowCred: 1,
+             origin: "*",
+           },
+           { pass: 1,
+             method: "GET",
+             setCookie: "a=1",
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             cookie: "a=1",
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             noCookie: 1,
+             withCred: 0,
+             allowCred: 1,
+           },
+           { pass: 0,
+             method: "GET",
+             noCookie: 1,
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             setCookie: "a=2",
+             withCred: 0,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             cookie: "a=1",
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             setCookie: "a=2",
+             withCred: 1,
+             allowCred: 1,
+           },
+           { pass: 1,
+             method: "GET",
+             cookie: "a=2",
+             withCred: 1,
+             allowCred: 1,
+           },
+           ];
+           // FIXME(nsm): Add "same-origin" credentials test
+
+  var baseURL = "http://example.org" + corsServerPath;
+  var origin = "http://mochi.test:8888";
+
+  var finalPromiseResolve, finalPromiseReject;
+  var finalPromise = new Promise(function(res, rej) {
+    finalPromiseResolve = res;
+    finalPromiseReject = rej;
+  });
+
+  function makeRequest(test) {
+    req = {
+      url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+      method: test.method,
+      headers: test.headers,
+      withCred: test.withCred,
+    };
+
+    if (test.allowCred)
+      req.url += "&allowCred";
+
+    if (test.setCookie)
+      req.url += "&setCookie=" + escape(test.setCookie);
+    if (test.cookie)
+      req.url += "&cookie=" + escape(test.cookie);
+    if (test.noCookie)
+      req.url += "&noCookie";
+
+    if ("allowHeaders" in test)
+      req.url += "&allowHeaders=" + escape(test.allowHeaders);
+    if ("allowMethods" in test)
+      req.url += "&allowMethods=" + escape(test.allowMethods);
+
+    return new Request(req.url, { method: req.method,
+                                  headers: req.headers,
+                                  credentials: req.withCred ? "include" : "omit" });
+  }
+
+  function testResponse(res, test) {
+    if (test.pass) {
+      is(isNetworkError(res), false,
+        "shouldn't have failed in test for " + test.toSource());
+      is(res.status, 200, "wrong status in test for " + test.toSource());
+      is(res.statusText, "OK", "wrong status text for " + test.toSource());
+      return res.text().then(function(v) {
+        is(v, "<res>hello pass</res>\n",
+         "wrong text in test for " + test.toSource());
+      });
+    }
+    else {
+      is(isNetworkError(res), true,
+        "should have failed in test for " + test.toSource());
+      return res.text().then(function(v) {
+        is(v, "",
+         "wrong text in test for " + test.toSource());
+      });
+    }
+  }
+
+  function runATest(i) {
+    var test = tests[i];
+    var request = makeRequest(test);
+    fetch(request).then(function(res) {
+      testResponse(res, test);
+      if (i < tests.length-1) {
+        runATest(i+1);
+      } else {
+        finalPromiseResolve();
+      }
+    }, finalPromiseReject);
+  }
+
+  runATest(0);
+  return finalPromise;
+}
+
+function testRedirects() {
+  var origin = "http://mochi.test:8888";
+
+  var tests = [
+           { pass: 1,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://mochi.test:8888",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 1,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://mochi.test:8888",
+                    },
+                    ],
+           },
+           { pass: 1,
+             method: "GET",
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://mochi.test:8888",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8000",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8000",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    ],
+           },
+           { pass: 1,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8000",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: "x"
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8000",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "GET",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin
+                    },
+                    { server: "http://test2.mochi.test:8000",
+                      allowOrigin: origin
+                    },
+                    { server: "http://sub2.xn--lt-uia.mochi.test:8888",
+                      allowOrigin: "*"
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                    },
+                    ],
+           },
+           { pass: 1,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain" },
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin,
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin,
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin,
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin,
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.com",
+                    },
+                    { server: "http://sub1.test1.mochi.test:8888",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           { pass: 1,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain" },
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    { server: "http://mochi.test:8888",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           ];
+
+  var fetches = [];
+  for (test of tests) {
+    req = {
+      url: test.hops[0].server + corsServerPath + "hop=1&hops=" +
+           escape(test.hops.toSource()),
+      method: test.method,
+      headers: test.headers,
+      body: test.body,
+    };
+
+    if (test.pass) {
+      if (test.body)
+        req.url += "&body=" + escape(test.body);
+    }
+
+    var request = new Request(req.url, { method: req.method,
+                                         headers: req.headers,
+                                         body: req.body });
+    fetches.push((function(request, test) {
+      return fetch(request).then(function(res) {
+        if (test.pass) {
+          is(isNetworkError(res), false,
+            "shouldn't have failed in test for " + test.toSource());
+          is(res.status, 200, "wrong status in test for " + test.toSource());
+          is(res.statusText, "OK", "wrong status text for " + test.toSource());
+          is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
+          return res.text().then(function(v) {
+            is(v, "<res>hello pass</res>\n",
+               "wrong responseText in test for " + test.toSource());
+          });
+        }
+        else {
+          is(isNetworkError(res), true,
+            "should have failed in test for " + test.toSource());
+          is(res.status, 0, "wrong status in test for " + test.toSource());
+          is(res.statusText, "", "wrong status text for " + test.toSource());
+          return res.text().then(function(v) {
+            is(v, "",
+               "wrong responseText in test for " + test.toSource());
+          });
+        }
+      });
+    })(request, test));
+  }
+
+  return Promise.all(fetches);
+}
+
+function runTest() {
+  var done = function() {
+    if (typeof SimpleTest === "object") {
+      SimpleTest.finish();
+    } else {
+      postMessage({ type: 'finish' });
+    }
+  }
+
+  testNoCorsCtor();
+
+  Promise.resolve()
+    .then(testModeSameOrigin)
+    .then(testModeNoCors)
+    .then(testModeCors)
+    .then(testCredentials)
+    .then(testRedirects)
+    // Put more promise based tests here.
+    .then(done)
+    .catch(function(e) {
+      ok(false, "Some test failed " + e);
+      done();
+    });
+}
+
+onmessage = runTest;
--- a/dom/webidl/MediaKeyMessageEvent.webidl
+++ b/dom/webidl/MediaKeyMessageEvent.webidl
@@ -5,19 +5,25 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
+enum MediaKeyMessageType {
+  "license-request",
+  "license-renewal",
+  "license-release"
+};
+
 [Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
 interface MediaKeyMessageEvent : Event {
+  readonly attribute MediaKeyMessageType messageType;
   [Throws]
   readonly attribute ArrayBuffer message;
-  readonly attribute DOMString? destinationURL;
 };
 
 dictionary MediaKeyMessageEventInit : EventInit {
+  MediaKeyMessageType messageType = "license-request";
   ArrayBuffer message;
-  DOMString? destinationURL = null;
 };
--- a/dom/webidl/Response.webidl
+++ b/dom/webidl/Response.webidl
@@ -7,17 +7,18 @@
  * https://fetch.spec.whatwg.org/#response-class
  */
 
 [Constructor(optional BodyInit body, optional ResponseInit init),
  Exposed=(Window,Worker),
  Func="mozilla::dom::Headers::PrefEnabled"]
 interface Response {
   [NewObject] static Response error();
-  [NewObject] static Response redirect(USVString url, optional unsigned short status = 302);
+  [Throws,
+   NewObject] static Response redirect(USVString url, optional unsigned short status = 302);
 
   readonly attribute ResponseType type;
 
   readonly attribute USVString url;
   readonly attribute unsigned short status;
   readonly attribute ByteString statusText;
   [SameObject] readonly attribute Headers headers;
 
--- a/dom/workers/WorkerThread.cpp
+++ b/dom/workers/WorkerThread.cpp
@@ -63,25 +63,25 @@ private:
 
   NS_DECL_NSITHREADOBSERVER
 };
 
 WorkerThread::WorkerThread()
   : nsThread(nsThread::NOT_MAIN_THREAD, kWorkerStackSize)
   , mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar")
   , mWorkerPrivate(nullptr)
-  , mOtherThreadDispatchingViaEventTarget(false)
+  , mOtherThreadsDispatchingViaEventTarget(0)
   , mAcceptingNonWorkerRunnables(true)
 {
 }
 
 WorkerThread::~WorkerThread()
 {
   MOZ_ASSERT(!mWorkerPrivate);
-  MOZ_ASSERT(!mOtherThreadDispatchingViaEventTarget);
+  MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget);
   MOZ_ASSERT(mAcceptingNonWorkerRunnables);
 }
 
 // static
 already_AddRefed<WorkerThread>
 WorkerThread::Create(const WorkerThreadFriendKey& /* aKey */)
 {
   MOZ_ASSERT(nsThreadManager::get());
@@ -118,21 +118,21 @@ WorkerThread::SetWorker(const WorkerThre
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver)));
     mObserver = nullptr;
 
     {
       MutexAutoLock lock(mLock);
 
       MOZ_ASSERT(mWorkerPrivate);
       MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
-      MOZ_ASSERT(!mOtherThreadDispatchingViaEventTarget,
+      MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget,
                  "XPCOM Dispatch hapenning at the same time our thread is "
                  "being unset! This should not be possible!");
 
-      while (mOtherThreadDispatchingViaEventTarget) {
+      while (mOtherThreadsDispatchingViaEventTarget) {
         mWorkerPrivateCondVar.Wait();
       }
 
       mAcceptingNonWorkerRunnables = true;
       mWorkerPrivate = nullptr;
     }
   }
 }
@@ -229,24 +229,24 @@ WorkerThread::Dispatch(nsIRunnable* aRun
     // No need to lock here because it is only modified on this thread.
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     workerPrivate = mWorkerPrivate;
   } else {
     MutexAutoLock lock(mLock);
 
-    MOZ_ASSERT(!mOtherThreadDispatchingViaEventTarget);
+    MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX);
 
     if (mWorkerPrivate) {
       workerPrivate = mWorkerPrivate;
 
-      // Setting this flag will make the worker thread sleep if it somehow tries
-      // to unset mWorkerPrivate while we're using it.
-      mOtherThreadDispatchingViaEventTarget = true;
+      // Incrementing this counter will make the worker thread sleep if it
+      // somehow tries to unset mWorkerPrivate while we're using it.
+      mOtherThreadsDispatchingViaEventTarget++;
     }
   }
 
   nsIRunnable* runnableToDispatch;
   nsRefPtr<WorkerRunnable> workerRunnable;
 
   if (aRunnable && onWorkerThread) {
     workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(aRunnable);
@@ -265,20 +265,21 @@ WorkerThread::Dispatch(nsIRunnable* aRun
 
       workerPrivate->mCondVar.Notify();
     }
 
     // Now unset our waiting flag.
     {
       MutexAutoLock lock(mLock);
 
-      MOZ_ASSERT(mOtherThreadDispatchingViaEventTarget);
-      mOtherThreadDispatchingViaEventTarget = false;
+      MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget);
 
-      mWorkerPrivateCondVar.Notify();
+      if (!--mOtherThreadsDispatchingViaEventTarget) {
+        mWorkerPrivateCondVar.Notify();
+      }
     }
   }
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
--- a/dom/workers/WorkerThread.h
+++ b/dom/workers/WorkerThread.h
@@ -47,17 +47,17 @@ class WorkerThread MOZ_FINAL
 
   // Protected by nsThread::mLock.
   WorkerPrivate* mWorkerPrivate;
 
   // Only touched on the target thread.
   nsRefPtr<Observer> mObserver;
 
   // Protected by nsThread::mLock and waited on with mWorkerPrivateCondVar.
-  bool mOtherThreadDispatchingViaEventTarget;
+  uint32_t mOtherThreadsDispatchingViaEventTarget;
 
   // Protected by nsThread::mLock.
   DebugOnly<bool> mAcceptingNonWorkerRunnables;
 
 public:
   static already_AddRefed<WorkerThread>
   Create(const WorkerThreadFriendKey& aKey);
 
--- a/dom/workers/test/fetch/worker_test_response.js
+++ b/dom/workers/test/fetch/worker_test_response.js
@@ -24,16 +24,39 @@ function testClone() {
               headers: { "Content-Length": 5 },
             })).clone();
   is(res.status, 404, "Response status is 404");
   is(res.statusText, "Not Found", "Response statusText is POST");
   ok(res.headers instanceof Headers, "Response should have non-null Headers object");
   is(res.headers.get('content-length'), "5", "Response content-length should be 5.");
 }
 
+function testRedirect() {
+  var res = Response.redirect("./redirect.response");
+  is(res.status, 302, "Default redirect has status code 302");
+  var h = res.headers.get("location");
+  ok(h === (new URL("./redirect.response", self.location.href)).href, "Location header should be correct absolute URL");
+
+  var successStatus = [301, 302, 303, 307, 308];
+  for (var i = 0; i < successStatus.length; ++i) {
+    var res = Response.redirect("./redirect.response", successStatus[i]);
+    is(res.status, successStatus[i], "Status code should match");
+  }
+
+  var failStatus = [300, 0, 304, 305, 306, 309, 500];
+  for (var i = 0; i < failStatus.length; ++i) {
+    try {
+      var res = Response.redirect(".", failStatus[i]);
+      ok(false, "Invalid status code should fail " + failStatus[i]);
+    } catch(e) {
+      is(e.name, "RangeError", "Invalid status code should fail " + failStatus[i]);
+    }
+  }
+}
+
 function testBodyUsed() {
   var res = new Response("Sample body");
   ok(!res.bodyUsed, "bodyUsed is initially false.");
   return res.text().then((v) => {
     is(v, "Sample body", "Body should match");
     ok(res.bodyUsed, "After reading body, bodyUsed should be true.");
   }).then(() => {
     return res.blob().then((v) => {
@@ -110,16 +133,17 @@ function testBodyExtraction() {
   })
 }
 
 onmessage = function() {
   var done = function() { postMessage({ type: 'finish' }) }
 
   testDefaultCtor();
   testClone();
+  testRedirect();
 
   Promise.resolve()
     .then(testBodyCreation)
     .then(testBodyUsed)
     .then(testBodyExtraction)
     // Put more promise based tests here.
     .then(done)
     .catch(function(e) {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -761,23 +761,21 @@ XULDocument::AddBroadcastListenerFor(Ele
 
     static const PLDHashTableOps gOps = {
         PL_DHashAllocTable,
         PL_DHashFreeTable,
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         ClearBroadcasterMapEntry,
-        PL_DHashFinalizeStub,
         nullptr
     };
 
     if (! mBroadcasterMap) {
-        mBroadcasterMap =
-            PL_NewDHashTable(&gOps, nullptr, sizeof(BroadcasterMapEntry));
+        mBroadcasterMap = PL_NewDHashTable(&gOps, sizeof(BroadcasterMapEntry));
 
         if (! mBroadcasterMap) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
     }
 
     BroadcasterMapEntry* entry =
--- a/dom/xul/templates/nsContentSupportMap.cpp
+++ b/dom/xul/templates/nsContentSupportMap.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsContentSupportMap.h"
 #include "nsXULElement.h"
 
 void
 nsContentSupportMap::Init()
 {
-    PL_DHashTableInit(&mMap, PL_DHashGetStubOps(), nullptr, sizeof(Entry));
+    PL_DHashTableInit(&mMap, PL_DHashGetStubOps(), sizeof(Entry));
 }
 
 void
 nsContentSupportMap::Finish()
 {
     if (mMap.ops)
         PL_DHashTableFinish(&mMap);
 }
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -17,18 +17,17 @@ protected:
         nsIContent*     mTemplate;
     };
 
     PLDHashTable mTable;
 
     void
     Init()
     {
-        PL_DHashTableInit(&mTable, PL_DHashGetStubOps(), nullptr,
-                          sizeof(Entry));
+        PL_DHashTableInit(&mTable, PL_DHashGetStubOps(), sizeof(Entry));
     }
 
     void
     Finish() { PL_DHashTableFinish(&mTable); }
 
 public:
     nsTemplateMap() { Init(); }
 
--- a/embedding/components/commandhandler/nsCommandParams.cpp
+++ b/embedding/components/commandhandler/nsCommandParams.cpp
@@ -16,18 +16,17 @@ using namespace mozilla;
 
 const PLDHashTableOps nsCommandParams::sHashOps =
 {
   PL_DHashAllocTable,
   PL_DHashFreeTable,
   HashKey,
   HashMatchEntry,
   HashMoveEntry,
-  HashClearEntry,
-  PL_DHashFinalizeStub
+  HashClearEntry
 };
 
 NS_IMPL_ISUPPORTS(nsCommandParams, nsICommandParams)
 
 nsCommandParams::nsCommandParams()
 {
   // init the hash table later
 }
@@ -35,18 +34,17 @@ nsCommandParams::nsCommandParams()
 nsCommandParams::~nsCommandParams()
 {
   PL_DHashTableFinish(&mValuesHash);
 }
 
 nsresult
 nsCommandParams::Init()
 {
-  PL_DHashTableInit(&mValuesHash, &sHashOps, (void *)this,
-                    sizeof(HashEntry), 2);
+  PL_DHashTableInit(&mValuesHash, &sHashOps, sizeof(HashEntry), 2);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCommandParams::GetValueType(const char* aName, int16_t* aRetVal)
 {
   NS_ENSURE_ARG_POINTER(aRetVal);
 
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -384,20 +384,24 @@ size_t MacIOSurface::GetHeight() {
   return GetDevicePixelHeight() / intScaleFactor;
 }
 
 size_t MacIOSurface::GetPlaneCount() {
   return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
 }
 
 /*static*/ size_t MacIOSurface::GetMaxWidth() {
+  if (!MacIOSurfaceLib::isInit())
+    return -1;
   return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth);
 }
 
 /*static*/ size_t MacIOSurface::GetMaxHeight() {
+  if (!MacIOSurfaceLib::isInit())
+    return -1;
   return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
 }
 
 size_t MacIOSurface::GetDevicePixelWidth() {
   return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
 }
 
 size_t MacIOSurface::GetDevicePixelHeight() {
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -447,17 +447,26 @@ GLScreenBuffer::Swap(const gfx::IntSize&
     }
 
     if (ShouldPreserveBuffer() &&
         mFront &&
         mBack)
     {
         auto src  = mFront->Surf();
         auto dest = mBack->Surf();
+
+        //uint32_t srcPixel = ReadPixel(src);
+        //uint32_t destPixel = ReadPixel(dest);
+        //printf_stderr("Before: src: 0x%08x, dest: 0x%08x\n", srcPixel, destPixel);
+
         SharedSurface::ProdCopy(src, dest, mFactory.get());
+
+        //srcPixel = ReadPixel(src);
+        //destPixel = ReadPixel(dest);
+        //printf_stderr("After: src: 0x%08x, dest: 0x%08x\n", srcPixel, destPixel);
     }
 
     return true;
 }
 
 bool
 GLScreenBuffer::PublishFrame(const gfx::IntSize& size)
 {
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -548,10 +548,30 @@ ReadbackSharedSurface(SharedSurface* src
                 rowItr += 4;
             }
         }
     }
 
     return true;
 }
 
+uint32_t
+ReadPixel(SharedSurface* src)
+{
+    GLContext* gl = src->mGL;
+
+    uint32_t pixel;
+
+    ScopedReadbackFB a(src);
+    {
+        ScopedPackAlignment autoAlign(gl, 4);
+
+        UniquePtr<uint8_t[]> bytes(new uint8_t[4]);
+        gl->raw_fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
+                            bytes.get());
+        memcpy(&pixel, bytes.get(), 4);
+    }
+
+    return pixel;
+}
+
 } /* namespace gfx */
 } /* namespace mozilla */
--- a/gfx/gl/SharedSurface.h
+++ b/gfx/gl/SharedSurface.h
@@ -284,13 +284,14 @@ class ScopedReadbackFB
     SharedSurface* mSurfToLock;
 
 public:
     explicit ScopedReadbackFB(SharedSurface* src);
     ~ScopedReadbackFB();
 };
 
 bool ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst);
+uint32_t ReadPixel(SharedSurface* src);
 
 } // namespace gl
 } // namespace mozilla
 
 #endif // SHARED_SURFACE_H_
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/Compositor.h"
 #include "base/message_loop.h"          // for MessageLoop
 #include "mozilla/layers/CompositorParent.h"  // for CompositorParent
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "gfx2DGlue.h"
+#include "nsAppRunner.h"
 
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 }
 
 namespace layers {
 
@@ -23,17 +24,17 @@ Compositor::GetBackend()
 {
   AssertOnCompositorThread();
   return sBackend;
 }
 
 /* static */ void
 Compositor::SetBackend(LayersBackend backend)
 {
-  if (sBackend != LayersBackend::LAYERS_NONE && sBackend != backend) {
+  if (!gIsGtest && sBackend != LayersBackend::LAYERS_NONE && sBackend != backend) {
     // Assert this once we figure out bug 972891.
     //MOZ_CRASH("Trying to use more than one OMTC compositor.");
 
 #ifdef XP_MACOSX
     printf("ERROR: Changing compositor from %u to %u.\n",
            unsigned(sBackend), unsigned(backend));
 #endif
   }
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "LayerScope.h"
 
+#include "nsAppRunner.h"
 #include "Composer2D.h"
 #include "Effects.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Endian.h"
 #include "TexturePoolOGL.h"
 #include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/CompositorParent.h"
@@ -976,17 +977,17 @@ LayerScope::SendLayerDump(UniquePtr<Pack
     WebSocketHelper::GetSocketManager()->AppendDebugData(
         new DebugGLLayersData(Move(aPacket)));
 }
 
 bool
 LayerScope::CheckSendable()
 {
     // Only compositor threads check LayerScope status
-    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread() || gIsGtest);
 
     if (!gfxPrefs::LayerScopeEnabled()) {
         return false;
     }
     if (!WebSocketHelper::GetSocketManager()) {
         Init();
         return false;
     }
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -65,20 +65,31 @@ static gfx::Matrix4x4
 GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor)
 {
   gfx::Matrix4x4 transform;
   const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent();
   for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM);
        ancestorParent ? iter != ancestorParent : iter.IsValid();
        iter = iter.GetParent()) {
     transform = transform * iter.GetTransform();
-    // If the layer has a pres shell resolution, the compositor will apply
-    // a scale to scale to this transform. Apply it here too.
-    const FrameMetrics& metrics = iter.Metrics();
-    transform.PostScale(metrics.mPresShellResolution, metrics.mPresShellResolution, 1.f);
+
+    if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+      // When scrolling containers, layout adds a post-scale into the transform
+      // of the displayport-ancestor (which we pick up in GetTransform() above)
+      // to cancel out the pres shell resolution (for historical reasons). The
+      // compositor in turn cancels out this post-scale (i.e., scales by the
+      // pres shell resolution), and to get correct calculations, we need to do
+      // so here, too.
+      //
+      // With containerless scrolling, the offending post-scale is on the
+      // parent layer of the displayport-ancestor, which we don't reach in this
+      // loop, so we don't need to worry about it.
+      const FrameMetrics& metrics = iter.Metrics();
+      transform.PostScale(metrics.mPresShellResolution, metrics.mPresShellResolution, 1.f);
+    }
   }
   return transform;
 }
 
 void
 ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
                                           LayerMetricsWrapper* aOutDisplayPortAncestor)
 {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersTypes.h"  // for etc
 #include "ipc/CompositorBench.h"        // for CompositorBench
 #include "ipc/ShadowLayerUtils.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
+#include "nsAppRunner.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING, NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
@@ -327,39 +328,45 @@ LayerManagerComposite::CreateOptimalMask
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
 
 already_AddRefed<PaintedLayer>
 LayerManagerComposite::CreatePaintedLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<ContainerLayer>
 LayerManagerComposite::CreateContainerLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<ImageLayer>
 LayerManagerComposite::CreateImageLayer()
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
 
 already_AddRefed<ColorLayer>
 LayerManagerComposite::CreateColorLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<ColorLayer> layer = new ColorLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<CanvasLayer>
 LayerManagerComposite::CreateCanvasLayer()
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -187,24 +187,16 @@ CompositorChild::ActorDestroy(ActorDestr
   // use.
   sCompositor = nullptr;
 
   MessageLoop::current()->PostTask(
     FROM_HERE,
     NewRunnableMethod(this, &CompositorChild::Release));
 }
 
-void
-CompositorChild::ShutDown()
-{
-  if (sCompositor) {
-    sCompositor->ActorDestroy(NormalShutdown);
-  }
-}
-
 bool
 CompositorChild::RecvSharedCompositorFrameMetrics(
     const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
     const CrossProcessMutexHandle& handle,
     const uint32_t& aAPZCId)
 {
   SharedFrameMetricsData* data = new SharedFrameMetricsData(metrics, handle, aAPZCId);
   mFrameMetricsTable.Put(data->GetViewID(), data);
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -75,18 +75,16 @@ public:
    * When we get that message, we bounce it to the TabParent via
    * the TabChild
    * @param tabChild The object to bounce the note to.  Non-NULL.
    */
   void RequestNotifyAfterRemotePaint(TabChild* aTabChild);
 
   void CancelNotifyAfterRemotePaint(TabChild* aTabChild);
 
-  static void ShutDown();
-
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorChild();
 
   virtual PLayerTransactionChild*
     AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
                                 const uint64_t& aId,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -849,18 +849,18 @@ CompositorParent::CompositeCallback(Time
   }
 
   mCurrentCompositeTask = nullptr;
   CompositeToTarget(nullptr);
 }
 
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
-static void
-SetShadowProperties(Layer* aLayer)
+/* static */ void
+CompositorParent::SetShadowProperties(Layer* aLayer)
 {
   // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   // Set the layerComposite's base transform to the layer's base transform.
   layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
   layerComposite->SetShadowTransformSetByAnimation(false);
   layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
   layerComposite->SetShadowClipRect(aLayer->GetClipRect());
@@ -1667,17 +1667,17 @@ CrossProcessCompositorParent::ShadowLaye
   if (!state) {
     return;
   }
   MOZ_ASSERT(state->mParent);
   state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
 
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
-    SetShadowProperties(shadowRoot);
+    CompositorParent::SetShadowProperties(shadowRoot);
   }
   UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
   state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
       aPaintSequenceNumber, aIsRepeatTransaction);
 
   // Send the 'remote paint ready' message to the content thread if it has already asked.
   if(mNotifyAfterRemotePaint)  {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -185,16 +185,18 @@ public:
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
   void ForceIsFirstPaint();
   void Destroy();
 
+  static void SetShadowProperties(Layer* aLayer);
+
   void NotifyChildCreated(const uint64_t& aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
   void ScheduleRenderOnCompositorThread();
   void SchedulePauseOnCompositorThread();
   /**
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4, Matrix
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
 #include "mozilla/layers/CompositingRenderTargetOGL.h"
 #include "mozilla/layers/Effects.h"     // for EffectChain, TexturedEffect, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
+#include "nsAppRunner.h"
 #include "nsAString.h"
 #include "nsIConsoleService.h"          // for nsIConsoleService, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsMathUtils.h"                // for NS_roundf
 #include "nsRect.h"                     // for nsIntRect
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsString, nsAutoCString, etc
@@ -99,16 +100,23 @@ CompositorOGL::~CompositorOGL()
   Destroy();
 }
 
 already_AddRefed<mozilla::gl::GLContext>
 CompositorOGL::CreateContext()
 {
   nsRefPtr<GLContext> context;
 
+  // Used by mock widget to create an offscreen context
+  void* widgetOpenGLContext = mWidget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT);
+  if (widgetOpenGLContext) {
+    GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
+    return already_AddRefed<GLContext>(alreadyRefed);
+  }
+
 #ifdef XP_WIN
   if (PR_GetEnv("MOZ_LAYERS_PREFER_EGL")) {
     printf_stderr("Trying GL layers...\n");
     context = gl::GLContextProviderEGL::CreateForWindow(mWidget);
   }
 #endif
 
   // Allow to create offscreen GL context for main Layer Manager
@@ -430,17 +438,17 @@ CompositorOGL::PrepareViewport(const gfx
   // drawing directly into the window's back buffer, so this keeps things
   // looking correct.
   // XXX: We keep track of whether the window size changed, so we could skip
   // this update if it hadn't changed since the last call.
 
   // Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
   // 2, 2) and flip the contents.
   Matrix viewMatrix;
-  if (mGLContext->IsOffscreen()) {
+  if (mGLContext->IsOffscreen() && !gIsGtest) {
     // In case of rendering via GL Offscreen context, disable Y-Flipping
     viewMatrix.PreTranslate(-1.0, -1.0);
     viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
   } else {
     viewMatrix.PreTranslate(-1.0, 1.0);
     viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
     viewMatrix.PreScale(1.0f, -1.0f);
   }
new file mode 100644
--- /dev/null
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -0,0 +1,267 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gfxUtils.h"
+#include "gtest/gtest.h"
+#include "TestLayers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
+#include "mozilla/layers/Compositor.h"  // for Compositor
+#include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "nsBaseWidget.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include <vector>
+
+const int gCompWidth = 256;
+const int gCompHeight = 256;
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+class MockWidget : public nsBaseWidget
+{
+public:
+  MockWidget() {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD              GetClientBounds(nsIntRect &aRect) {
+    aRect = nsIntRect(0, 0, gCompWidth, gCompHeight);
+    return NS_OK;
+  }
+  NS_IMETHOD              GetBounds(nsIntRect &aRect) { return GetClientBounds(aRect); }
+
+  void* GetNativeData(uint32_t aDataType) MOZ_OVERRIDE {
+    if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
+      mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
+      caps.preserve = false;
+      caps.bpp16 = false;
+      nsRefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
+        gfxIntSize(gCompWidth, gCompHeight), caps);
+      return context.forget().take();
+    }
+    return nullptr;
+  }
+
+  NS_IMETHOD              Create(nsIWidget *aParent,
+                                 nsNativeWidget aNativeParent,
+                                 const nsIntRect &aRect,
+                                 nsDeviceContext *aContext,
+                                 nsWidgetInitData *aInitData = nullptr) { return NS_OK; }
+  NS_IMETHOD              Show(bool aState) { return NS_OK; }
+  virtual bool            IsVisible() const { return true; }
+  NS_IMETHOD              ConstrainPosition(bool aAllowSlop,
+                                            int32_t *aX, int32_t *aY) { return NS_OK; }
+  NS_IMETHOD              Move(double aX, double aY) { return NS_OK; }
+  NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+  NS_IMETHOD              Resize(double aX, double aY,
+                                 double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+
+  NS_IMETHOD              Enable(bool aState) { return NS_OK; }
+  virtual bool            IsEnabled() const { return true; }
+  NS_IMETHOD              SetFocus(bool aRaise) { return NS_OK; }
+  virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { return NS_OK; }
+  NS_IMETHOD              Invalidate(const nsIntRect &aRect) { return NS_OK; }
+  NS_IMETHOD              SetTitle(const nsAString& title) { return NS_OK; }
+  virtual nsIntPoint      WidgetToScreenOffset() { return nsIntPoint(0, 0); }
+  NS_IMETHOD              DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+                                        nsEventStatus& aStatus) { return NS_OK; }
+  NS_IMETHOD              CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) { return NS_OK; }
+  NS_IMETHOD_(void)       SetInputContext(const InputContext& aContext,
+                                          const InputContextAction& aAction) {}
+  NS_IMETHOD_(InputContext) GetInputContext() { abort(); }
+  NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; }
+private:
+  ~MockWidget() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
+
+struct LayerManagerData {
+  RefPtr<MockWidget> mWidget;
+  RefPtr<Compositor> mCompositor;
+  nsRefPtr<LayerManagerComposite> mLayerManager;
+
+  LayerManagerData(Compositor* compositor, MockWidget* widget, LayerManagerComposite* layerManager)
+    : mWidget(widget)
+    , mCompositor(compositor)
+    , mLayerManager(layerManager)
+  {}
+};
+
+static TemporaryRef<Compositor> CreateTestCompositor(LayersBackend backend, MockWidget* widget)
+{
+  gfxPrefs::GetSingleton();
+
+  RefPtr<Compositor> compositor;
+
+  if (backend == LayersBackend::LAYERS_OPENGL) {
+    compositor = new CompositorOGL(widget,
+                                   gCompWidth,
+                                   gCompHeight,
+                                   true);
+    compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
+  } else if (backend == LayersBackend::LAYERS_BASIC) {
+    compositor = new BasicCompositor(widget);
+#ifdef XP_WIN
+  } else if (backend == LayersBackend::LAYERS_D3D11) {
+    //compositor = new CompositorD3D11();
+    MOZ_CRASH(); // No support yet
+  } else if (backend == LayersBackend::LAYERS_D3D9) {
+    //compositor = new CompositorD3D9(this, mWidget);
+    MOZ_CRASH(); // No support yet
+#endif
+  }
+
+  if (!compositor) {
+    printf_stderr("Failed to construct layer manager for the requested backend\n");
+    abort();
+  }
+
+  return compositor;
+}
+
+/**
+ * Get a list of layers managers for the platform to run the test on.
+ */
+static std::vector<LayerManagerData> GetLayerManagers(std::vector<LayersBackend> aBackends)
+{
+  std::vector<LayerManagerData> managers;
+
+  for (size_t i = 0; i < aBackends.size(); i++) {
+    auto backend = aBackends[i];
+
+    RefPtr<MockWidget> widget = new MockWidget();
+    RefPtr<Compositor> compositor = CreateTestCompositor(backend, widget);
+
+    nsRefPtr<LayerManagerComposite> layerManager = new LayerManagerComposite(compositor);
+
+    layerManager->Initialize();
+
+    managers.push_back(LayerManagerData(compositor, widget, layerManager));
+  }
+
+  return managers;
+}
+
+/**
+ * This will return the default list of backends that
+ * units test should run against.
+ */
+static std::vector<LayersBackend> GetPlatformBackends()
+{
+  std::vector<LayersBackend> backends;
+
+  // For now we only support Basic for gtest
+  backends.push_back(LayersBackend::LAYERS_BASIC);
+
+#ifdef XP_MACOSX
+  backends.push_back(LayersBackend::LAYERS_OPENGL);
+#endif
+
+  // TODO Support OGL/D3D backends with unit test
+  return backends;
+}
+
+static TemporaryRef<DrawTarget> CreateDT()
+{
+  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+    IntSize(gCompWidth, gCompHeight), SurfaceFormat::B8G8R8A8);
+
+  return dt;
+}
+
+static bool CompositeAndCompare(nsRefPtr<LayerManagerComposite> layerManager, DrawTarget* refDT)
+{
+  RefPtr<DrawTarget> drawTarget = CreateDT();
+
+  layerManager->BeginTransactionWithDrawTarget(drawTarget, nsIntRect(0, 0, gCompWidth, gCompHeight));
+  layerManager->EndEmptyTransaction();
+
+  RefPtr<SourceSurface> ss = drawTarget->Snapshot();
+  RefPtr<DataSourceSurface> dss = ss->GetDataSurface();
+  uint8_t* bitmap = dss->GetData();
+
+  RefPtr<SourceSurface> ssRef = refDT->Snapshot();
+  RefPtr<DataSourceSurface> dssRef = ssRef->GetDataSurface();
+  uint8_t* bitmapRef = dssRef->GetData();
+
+  for (int y = 0; y < gCompHeight; y++) {
+    for (int x = 0; x < gCompWidth; x++) {
+      for (size_t channel = 0; channel < 4; channel++) {
+        uint8_t bit = bitmap[y * dss->Stride() + x * 4 + channel];
+        uint8_t bitRef = bitmapRef[y * dss->Stride() + x * 4 + channel];
+        if (bit != bitRef) {
+          printf("Layer Tree:\n");
+          layerManager->Dump();
+          printf("Original:\n");
+          gfxUtils::DumpAsDataURI(drawTarget);
+          printf("\n\n");
+
+          printf("Reference:\n");
+          gfxUtils::DumpAsDataURI(refDT);
+          printf("\n\n");
+
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+TEST(Gfx, CompositorConstruct)
+{
+  auto layerManagers = GetLayerManagers(GetPlatformBackends());
+}
+
+TEST(Gfx, CompositorSimpleTree)
+{
+  auto layerManagers = GetLayerManagers(GetPlatformBackends());
+  for (size_t i = 0; i < layerManagers.size(); i++) {
+    nsRefPtr<LayerManagerComposite> layerManager = layerManagers[i].mLayerManager;
+    nsRefPtr<LayerManager> lmBase = layerManager.get();
+    nsTArray<nsRefPtr<Layer>> layers;
+    nsIntRegion layerVisibleRegion[] = {
+      nsIntRegion(nsIntRect(0, 0, gCompWidth, gCompHeight)),
+      nsIntRegion(nsIntRect(0, 0, gCompWidth, gCompHeight)),
+      nsIntRegion(nsIntRect(0, 0, 100, 100)),
+      nsIntRegion(nsIntRect(0, 50, 100, 100)),
+    };
+    nsRefPtr<Layer> root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+
+    { // background
+      ColorLayer* colorLayer = layers[1]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(1.f, 0.f, 1.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    {
+      ColorLayer* colorLayer = layers[2]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(1.f, 0.f, 0.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    {
+      ColorLayer* colorLayer = layers[3]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(0.f, 0.f, 1.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    RefPtr<DrawTarget> refDT = CreateDT();
+    refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
+    refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
+    refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
+    EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
+  }
+}
+
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -2,44 +2,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #include "TestLayers.h"
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 #include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/CompositorParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
-class TestLayerManager: public LayerManager {
-public:
-  TestLayerManager()
-    : LayerManager()
-  {}
-
-  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; }
-  virtual already_AddRefed<ContainerLayer> CreateContainerLayer() { return nullptr; }
-  virtual void GetBackendName(nsAString& aName) {}
-  virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
-  virtual void BeginTransaction() {}
-  virtual already_AddRefed<ImageLayer> CreateImageLayer() { return nullptr; }
-  virtual void SetRoot(Layer* aLayer) {}
-  virtual already_AddRefed<ColorLayer> CreateColorLayer() { return nullptr; }
-  virtual void BeginTransactionWithTarget(gfxContext* aTarget) {}
-  virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() { return nullptr; }
-  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
-                              void* aCallbackData,
-                              EndTransactionFlags aFlags = END_DEFAULT) {}
-  virtual int32_t GetMaxTextureSize() const { return 0; }
-  virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() { return nullptr; }
-};
-
 class TestContainerLayer: public ContainerLayer {
 public:
   explicit TestContainerLayer(LayerManager* aManager)
     : ContainerLayer(aManager, nullptr)
   {}
 
   virtual const char* Name() const {
     return "TestContainerLayer";
@@ -68,16 +46,54 @@ public:
     return TYPE_PAINTED;
   }
 
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     MOZ_CRASH();
   }
 };
 
+class TestLayerManager: public LayerManager {
+public:
+  TestLayerManager()
+    : LayerManager()
+  {}
+
+  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; }
+  virtual already_AddRefed<ContainerLayer> CreateContainerLayer() {
+    nsRefPtr<ContainerLayer> layer = new TestContainerLayer(this);
+    return layer.forget();
+  }
+  virtual void GetBackendName(nsAString& aName) {}
+  virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
+  virtual void BeginTransaction() {}
+  virtual already_AddRefed<ImageLayer> CreateImageLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
+    nsRefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
+    return layer.forget();
+  }
+  virtual already_AddRefed<ColorLayer> CreateColorLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual void SetRoot(Layer* aLayer) {}
+  virtual void BeginTransactionWithTarget(gfxContext* aTarget) {}
+  virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+                              void* aCallbackData,
+                              EndTransactionFlags aFlags = END_DEFAULT) {}
+  virtual int32_t GetMaxTextureSize() const { return 0; }
+};
+
 class TestUserData: public LayerUserData {
 public:
   MOCK_METHOD0(Die, void());
   virtual ~TestUserData() { Die(); }
 };
 
 
 TEST(Layers, LayerConstructor) {
@@ -148,33 +164,37 @@ TEST(Layers, UserData) {
   delete layerPtr;
 
 }
 
 static
 already_AddRefed<Layer> CreateLayer(char aLayerType, LayerManager* aManager) {
   nsRefPtr<Layer> layer = nullptr;
   if (aLayerType == 'c') {
-    layer = new TestContainerLayer(aManager);
+    layer = aManager->CreateContainerLayer();
   } else if (aLayerType == 't') {
-    layer = new TestPaintedLayer(aManager);
+    layer = aManager->CreatePaintedLayer();
+  } else if (aLayerType == 'o') {
+    layer = aManager->CreateColorLayer();
   }
   return layer.forget();
 }
 
 already_AddRefed<Layer> CreateLayerTree(
     const char* aLayerTreeDescription,
     nsIntRegion* aVisibleRegions,
     const Matrix4x4* aTransforms,
     nsRefPtr<LayerManager>& manager,
     nsTArray<nsRefPtr<Layer> >& aLayersOut) {
 
   aLayersOut.Clear();
 
-  manager = new TestLayerManager();
+  if (!manager) {
+    manager = new TestLayerManager();
+  }
 
   nsRefPtr<Layer> rootLayer = nullptr;
   nsRefPtr<ContainerLayer> parentContainerLayer = nullptr;
   nsRefPtr<Layer> lastLayer = nullptr;
   int layerNumber = 0;
   for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) {
     if (aLayerTreeDescription[i] == '(') {
       if (!lastLayer) {
@@ -210,16 +230,21 @@ already_AddRefed<Layer> CreateLayerTree(
         parentContainerLayer->InsertAfter(layer, parentContainerLayer->GetLastChild());
         layer->SetParent(parentContainerLayer);
       }
       lastLayer = layer;
     }
   }
   if (rootLayer) {
     rootLayer->ComputeEffectiveTransforms(Matrix4x4());
+    manager->SetRoot(rootLayer);
+    if (rootLayer->AsLayerComposite()) {
+      // Only perform this for LayerManagerComposite
+      CompositorParent::SetShadowProperties(rootLayer);
+    }
   }
   return rootLayer.forget();
 }
 
 TEST(Layers, LayerTree) {
   const char* layerTreeSyntax = "c(c(tt))";
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -6,16 +6,17 @@
 
 UNIFIED_SOURCES += [
     'gfxSurfaceRefCountTest.cpp',
     # Disabled on suspicion of causing bug 904227
     #'gfxWordCacheTest.cpp',
     'TestAsyncPanZoomController.cpp',
     'TestBufferRotation.cpp',
     'TestColorNames.cpp',
+    'TestCompositor.cpp',
     'TestGfxPrefs.cpp',
     'TestLayers.cpp',
     'TestRegion.cpp',
     'TestSkipChars.cpp',
     # Hangs on linux in ApplyGdkScreenFontOptions
     #'gfxFontSelectionTest.cpp',
     'TestTextures.cpp',
     # Test works but it doesn't assert anything
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -623,21 +623,20 @@ public:
     {
         mOps = (PLDHashTableOps) {
             PL_DHashAllocTable,
             PL_DHashFreeTable,
             StringHash,
             HashMatchEntry,
             MoveEntry,
             PL_DHashClearEntryStub,
-            PL_DHashFinalizeStub,
             nullptr
         };
 
-        PL_DHashTableInit(&mMap, &mOps, nullptr, sizeof(FNCMapEntry), 0);
+        PL_DHashTableInit(&mMap, &mOps, sizeof(FNCMapEntry), 0);
 
         NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default,
                           "StartupCacheFontNameCache should only be used in chrome process");
         mCache = mozilla::scache::StartupCache::GetSingleton();
 
         Init();
     }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -667,29 +667,26 @@ gfxPlatform::InitLayersIPC()
 /* static */ void
 gfxPlatform::ShutdownLayersIPC()
 {
     if (!sLayersIPCIsUp) {
       return;
     }
     sLayersIPCIsUp = false;
 
-    GeckoProcessType processType = XRE_GetProcessType();
-    if (processType == GeckoProcessType_Default) {
+    if (XRE_GetProcessType() == GeckoProcessType_Default)
+    {
         // This must happen after the shutdown of media and widgets, which
         // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
         layers::ImageBridgeChild::ShutDown();
-
 #ifdef MOZ_WIDGET_GONK
         layers::SharedBufferManagerChild::ShutDown();
 #endif
 
         layers::CompositorParent::ShutDown();
-    } else if (processType == GeckoProcessType_Content) {
-        layers::CompositorChild::ShutDown();
     }
 }
 
 gfxPlatform::~gfxPlatform()
 {
     mScreenReferenceSurface = nullptr;
     mScreenReferenceDrawTarget = nullptr;
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1698,17 +1698,19 @@ gfxFontGroup::FindPlatformFont(const nsA
             family = mUserFontSet->LookupFamily(aName);
             if (family) {
                 nsAutoTArray<gfxFontEntry*,4> userfonts;
                 family->FindAllFontsForStyle(mStyle, userfonts, needsBold);
                 // add these to the fontlist
                 uint32_t count = userfonts.Length();
                 for (uint32_t i = 0; i < count; i++) {
                     fe = userfonts[i];
-                    mFonts.AppendElement(FamilyFace(family, fe, needsBold));
+                    FamilyFace ff(family, fe, needsBold);
+                    ff.CheckState(mSkipDrawing);
+                    mFonts.AppendElement(ff);
                 }
             }
         }
     }
 
     // Not known in the user font set ==> check system fonts
     if (!family) {
         gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1060,33 +1060,16 @@ gfxWindowsPlatform::DidRenderingDeviceRe
   if (GetD3D10Device()) {
     if (GetD3D10Device()->GetDeviceRemovedReason() != S_OK) {
       return true;
     }
   }
   return false;
 }
 
-gfxFontFamily *
-gfxWindowsPlatform::FindFontFamily(const nsAString& aName)
-{
-    return gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
-}
-
-gfxFontEntry *
-gfxWindowsPlatform::FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle)
-{
-    nsRefPtr<gfxFontFamily> ff = FindFontFamily(aName);
-    if (!ff)
-        return nullptr;
-
-    bool aNeedsBold;
-    return ff->FindFontForStyle(aFontStyle, aNeedsBold);
-}
-
 void
 gfxWindowsPlatform::GetPlatformCMSOutputProfile(void* &mem, size_t &mem_size)
 {
     WCHAR str[MAX_PATH];
     DWORD size = MAX_PATH;
     BOOL res;
 
     mem = nullptr;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -206,20 +206,16 @@ public:
 
     /**
      * Check whether format is supported on a platform or not (if unclear, returns true)
      */
     virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags);
 
     virtual bool DidRenderingDeviceReset();
 
-    /* Find a FontFamily/FontEntry object that represents a font on your system given a name */
-    gfxFontFamily *FindFontFamily(const nsAString& aName);
-    gfxFontEntry *FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle);
-
     // ClearType is not always enabled even when available (e.g. Windows XP)
     // if either of these prefs are enabled and apply, use ClearType rendering
     bool UseClearTypeForDownloadableFonts();
     bool UseClearTypeAlways();
 
     static void GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion);
 
     // returns ClearType tuning information for each display
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -302,16 +302,21 @@ EvalKernel(JSContext *cx, const CallArgs
                                              evalType == DIRECT_EVAL
                                              ? CALLED_FROM_JSOP_EVAL
                                              : NOT_CALLED_FROM_JSOP_EVAL);
 
         const char *introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
+        RootedObject enclosing(cx);
+        if (evalType == DIRECT_EVAL)
+            enclosing = callerScript->innermostStaticScope(pc);
+        Rooted<StaticEvalObject *> staticScope(cx, StaticEvalObject::create(cx, enclosing));
+
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
@@ -321,21 +326,24 @@ EvalKernel(JSContext *cx, const CallArgs
             return false;
 
         const char16_t *chars = flatChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                     scopeobj, callerScript, options,
-                                                     srcBuf, flatStr, staticLevel);
+                                                     scopeobj, callerScript, staticScope,
+                                                     options, srcBuf, flatStr, staticLevel);
         if (!compiled)
             return false;
 
+        if (compiled->strict())
+            staticScope->setStrict();
+
         esg.setNewScript(compiled);
     }
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
                          NullFramePtr() /* evalInFrame */, args.rval().address());
 }
 
 bool
@@ -376,16 +384,21 @@ js::DirectEvalStringFromIon(JSContext *c
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                               &mutedErrors, CALLED_FROM_JSOP_EVAL);
 
         const char *introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
+        RootedObject enclosing(cx, callerScript->innermostStaticScope(pc));
+        Rooted<StaticEvalObject *> staticScope(cx, StaticEvalObject::create(cx, enclosing));
+        if (!staticScope)
+            return false;
+
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(IsStrictEvalPC(pc));
@@ -395,21 +408,24 @@ js::DirectEvalStringFromIon(JSContext *c
             return false;
 
         const char16_t *chars = flatChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                     scopeobj, callerScript, options,
-                                                     srcBuf, flatStr, staticLevel);
+                                                     scopeobj, callerScript, staticScope,
+                                                     options, srcBuf, flatStr, staticLevel);
         if (!compiled)
             return false;
 
+        if (compiled->strict())
+            staticScope->setStrict();
+
         esg.setNewScript(compiled);
     }
 
     // Primitive 'this' values should have been filtered out by Ion. If boxed,
     // the calling frame cannot be updated to store the new object.
     MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull());
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL),
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -9,19 +9,17 @@
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsobj.h"
-#ifndef JS_MORE_DETERMINISTIC
 #include "jsprf.h"
-#endif
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSValidate.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2775,31 +2775,16 @@ case "$GRE_MILESTONE" in
       RELEASE_BUILD=1
       AC_DEFINE(RELEASE_BUILD)
       ;;
 esac
 AC_SUBST(NIGHTLY_BUILD)
 AC_SUBST(RELEASE_BUILD)
 
 dnl ========================================================
-dnl Disable compiling sources in unified mode.
-dnl ========================================================
-
-if test -z "$NIGHTLY_BUILD"; then
-    MOZ_DISABLE_UNIFIED_COMPILATION=1
-fi
-
-MOZ_ARG_DISABLE_BOOL(unified-compilation,
-[  --disable-unified-compilation
-                          Disable unified compilation of some C/C++ sources],
-    MOZ_DISABLE_UNIFIED_COMPILATION=1,
-    MOZ_DISABLE_UNIFIED_COMPILATION=)
-AC_SUBST(MOZ_DISABLE_UNIFIED_COMPILATION)
-
-dnl ========================================================
 dnl =
 dnl = Check for external package dependencies
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(External Packages)
 
 MOZ_CONFIG_NSPR(js)
 
--- a/js/src/doc/Debugger/Debugger.Environment.md
+++ b/js/src/doc/Debugger/Debugger.Environment.md
@@ -89,16 +89,22 @@ properties from its prototype:
 
 `callee`
 :   If this environment represents the variable environment (the top-level
     environment within the function, which receives `var` definitions) for
     a call to a function <i>f</i>, then this property's value is a
     [`Debugger.Object`][object] instance referring to <i>f</i>. Otherwise,
     this property's value is `null`.
 
+`optimizedOut`
+:   True if this environment is optimized out. False otherwise. For example,
+    functions whose locals are never aliased may present optimized-out
+    environments. When true, `getVariable` returns an ordinary JavaScript
+    object whose `optimizedOut` property is true on all bindings, and
+    `setVariable` throws a `ReferenceError`.
 
 
 ## Function Properties of the Debugger.Environment Prototype Object
 
 The methods described below may only be called with a `this` value
 referring to a `Debugger.Environment` instance; they may not be used as
 methods of other kinds of objects.
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -205,16 +205,17 @@ frontend::CreateScriptSourceObject(Exclu
     }
 
     return sso;
 }
 
 JSScript *
 frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain,
                         HandleScript evalCaller,
+                        Handle<StaticEvalObject *> evalStaticScope,
                         const ReadOnlyCompileOptions &options,
                         SourceBufferHolder &srcBuf,
                         JSString *source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask *extraSct /* = nullptr */)
 {
     MOZ_ASSERT(srcBuf.get());
 
@@ -279,33 +280,33 @@ frontend::CompileScript(ExclusiveContext
 
     if (!parser.checkOptions())
         return nullptr;
 
     Directives directives(options.strictOption);
     GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption);
 
     bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
-    Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
+    Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
                                                   options, staticLevel, sourceObject, 0,
                                                   srcBuf.length()));
     if (!script)
         return nullptr;
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalScope =
         scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr;
     MOZ_ASSERT_IF(globalScope, globalScope->isNative());
     MOZ_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
 
     BytecodeEmitter::EmitterMode emitterMode =
         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script,
                         /* lazyScript = */ js::NullPtr(), options.forEval,
-                        evalCaller, !!globalScope, options.lineno, emitterMode);
+                        evalCaller, evalStaticScope, !!globalScope, options.lineno, emitterMode);
     if (!bce.init())
         return nullptr;
 
     // Syntax parsing may cause us to restart processing of top level
     // statements in the script. Use Maybe<> so that the parse context can be
     // reset when this occurs.
     Maybe<ParseContext<FullParseHandler> > pc;
 
@@ -511,16 +512,17 @@ frontend::CompileLazyFunction(JSContext 
         script->setDirectlyInsideEval();
     if (lazy->usesArgumentsApplyAndThis())
         script->setUsesArgumentsApplyAndThis();
     if (lazy->hasBeenCloned())
         script->setHasBeenCloned();
 
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
                         options.forEval, /* evalCaller = */ js::NullPtr(),
+                        /* evalStaticScope = */ js::NullPtr(),
                         /* hasGlobalScope = */ true, options.lineno,
                         BytecodeEmitter::LazyFunction);
     if (!bce.init())
         return false;
 
     return EmitFunctionScript(cx, &bce, pn->pn_body);
 }
 
@@ -643,16 +645,17 @@ CompileFunctionBody(JSContext *cx, Mutab
          * consumers of JS::CompileFunction, namely
          * EventListenerManager::CompileEventHandlerInternal, passes in a
          * nullptr environment. This compiled function is never used, but
          * instead is cloned immediately onto the right scope chain.
          */
         BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script,
                                /* lazyScript = */ js::NullPtr(), /* insideEval = */ false,
                                /* evalCaller = */ js::NullPtr(),
+                               /* evalStaticScope = */ js::NullPtr(),
                                fun->environment() && fun->environment()->is<GlobalObject>(),
                                options.lineno);
         if (!funbce.init())
             return false;
 
         if (!EmitFunctionScript(cx, &funbce, fn->pn_body))
             return false;
     } else {
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -12,23 +12,25 @@
 class JSLinearString;
 
 namespace js {
 
 class AutoNameVector;
 class LazyScript;
 class LifoAlloc;
 class ScriptSourceObject;
+class StaticEvalObject;
 struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript *
 CompileScript(ExclusiveContext *cx, LifoAlloc *alloc,
               HandleObject scopeChain, HandleScript evalCaller,
+              Handle<StaticEvalObject *> evalStaticScope,
               const ReadOnlyCompileOptions &options, SourceBufferHolder &srcBuf,
               JSString *source_ = nullptr, unsigned staticLevel = 0,
               SourceCompressionTask *extraSct = nullptr);
 
 bool
 CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const char16_t *chars, size_t length);
 
 /*
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -111,26 +111,28 @@ struct LoopStmtInfo : public StmtInfoBCE
 };
 
 } // anonymous namespace
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
                                  Parser<FullParseHandler> *parser, SharedContext *sc,
                                  HandleScript script, Handle<LazyScript *> lazyScript,
                                  bool insideEval, HandleScript evalCaller,
+                                 Handle<StaticEvalObject *> staticEvalScope,
                                  bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     parent(parent),
     script(sc->context, script),
     lazyScript(sc->context, lazyScript),
     prolog(sc->context, lineNum),
     main(sc->context, lineNum),
     current(&main),
     parser(parser),
     evalCaller(evalCaller),
+    evalStaticScope(staticEvalScope),
     topStmt(nullptr),
     topScopeStmt(nullptr),
     staticScope(sc->context),
     atomIndices(sc->context),
     firstLine(lineNum),
     localsToFrameSlots_(sc->context),
     stackDepth(0), maxStackDepth(0),
     arrayCompDepth(0),
@@ -797,17 +799,20 @@ PushLoopStatement(BytecodeEmitter *bce, 
 static JSObject *
 EnclosingStaticScope(BytecodeEmitter *bce)
 {
     if (bce->staticScope)
         return bce->staticScope;
 
     if (!bce->sc->isFunctionBox()) {
         MOZ_ASSERT(!bce->parent);
-        return nullptr;
+
+        // Top-level eval scripts have a placeholder static scope so that
+        // StaticScopeIter may iterate through evals.
+        return bce->evalStaticScope;
     }
 
     return bce->sc->asFunctionBox()->function();
 }
 
 #ifdef DEBUG
 static bool
 AllLocalsAliased(StaticBlockObject &obj)
@@ -1586,18 +1591,18 @@ TryConvertFreeName(BytecodeEmitter *bce,
             hops++;
             if (funbox->function()->isNamedLambda())
                 hops++;
         }
         if (bce->script->directlyInsideEval())
             return false;
         RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
         for (StaticScopeIter<CanGC> ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
-            if (ssi.type() != StaticScopeIter<CanGC>::FUNCTION) {
-                if (ssi.type() == StaticScopeIter<CanGC>::BLOCK) {
+            if (ssi.type() != StaticScopeIter<CanGC>::Function) {
+                if (ssi.type() == StaticScopeIter<CanGC>::Block) {
                     // Use generic ops if a catch block is encountered.
                     return false;
                 }
                 if (ssi.hasDynamicScopeObject())
                     hops++;
                 continue;
             }
             RootedScript script(bce->sc->context, ssi.funScript());
@@ -5334,19 +5339,17 @@ EmitFunc(ExclusiveContext *cx, BytecodeE
             fun->isInterpreted() &&
             (bce->checkSingletonContext() ||
              (!bce->isInLoop() && bce->isRunOnceLambda()));
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
 
         if (fun->isInterpretedLazy()) {
             if (!fun->lazyScript()->sourceObject()) {
-                JSObject *scope = bce->staticScope;
-                if (!scope && bce->sc->isFunctionBox())
-                    scope = bce->sc->asFunctionBox()->function();
+                JSObject *scope = EnclosingStaticScope(bce);
                 JSObject *source = bce->script->sourceObject();
                 fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
             }
             if (bce->emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             SharedContext *outersc = bce->sc;
 
@@ -5372,18 +5375,18 @@ EmitFunc(ExclusiveContext *cx, BytecodeE
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
                 return false;
 
             script->bindings = funbox->bindings;
 
             uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
             BytecodeEmitter bce2(bce, bce->parser, funbox, script, /* lazyScript = */ js::NullPtr(),
-                                 bce->insideEval, bce->evalCaller, bce->hasGlobalScope, lineNum,
-                                 bce->emitterMode);
+                                 bce->insideEval, bce->evalCaller, bce->evalStaticScope,
+                                 bce->hasGlobalScope, lineNum, bce->emitterMode);
             if (!bce2.init())
                 return false;
 
             /* We measured the max scope depth when we parsed the function. */
             if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -14,16 +14,19 @@
 #include "jscntxt.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 
 #include "frontend/ParseMaps.h"
 #include "frontend/SourceNotes.h"
 
 namespace js {
+
+class StaticEvalObject;
+
 namespace frontend {
 
 class FullParseHandler;
 class ObjectBox;
 class ParseNode;
 template <typename ParseHandler> class Parser;
 class SharedContext;
 class TokenStream;
@@ -111,16 +114,18 @@ struct BytecodeEmitter
         {}
     };
     EmitSection prolog, main, *current;
 
     /* the parser */
     Parser<FullParseHandler> *const parser;
 
     HandleScript    evalCaller;     /* scripted caller info for eval and dbgapi */
+    Handle<StaticEvalObject *> evalStaticScope;
+                                   /* compile time scope for eval; does not imply stmt stack */
 
     StmtInfoBCE     *topStmt;       /* top of statement info stack */
     StmtInfoBCE     *topScopeStmt;  /* top lexical scope statement */
     Rooted<NestedScopeObject *> staticScope;
                                     /* compile time scope chain */
 
     OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
@@ -192,17 +197,18 @@ struct BytecodeEmitter
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
     BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
                     HandleScript script, Handle<LazyScript *> lazyScript,
-                    bool insideEval, HandleScript evalCaller, bool hasGlobalScope,
+                    bool insideEval, HandleScript evalCaller,
+                    Handle<StaticEvalObject *> evalStaticScope, bool hasGlobalScope,
                     uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
     bool updateLocalsToFrameSlots();
 
     bool isAliasedName(ParseNode *pn);
 
     MOZ_ALWAYS_INLINE
     bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
--- a/js/src/jit-test/tests/baseline/bug852801.js
+++ b/js/src/jit-test/tests/baseline/bug852801.js
@@ -1,9 +1,9 @@
-// |jit-test| allow-oom
+// |jit-test| allow-oom; allow-unhandlable-oom
 var STATUS = "STATUS: ";
 var callStack = new Array();
 function startTest() { }
 function TestCase(n, d, e, a) {
     this.name = n;
 }
 TestCase.prototype.dump = function () {};
 TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; });
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-find-07.js
@@ -0,0 +1,22 @@
+// We can find into and from optimized out scopes.
+
+var g = newGlobal();
+var dbg = new Debugger;
+dbg.addDebuggee(g);
+
+g.eval("" + function f() {
+  var x = 42;
+  function g() { }
+  g();
+});
+
+dbg.onEnterFrame = function (f) {
+  if (f.callee && (f.callee.name === "g")) {
+    genv = f.environment.parent;
+    assertEq(genv.optimizedOut, true);
+    assertEq(genv.find("f").type, "object");
+    assertEq(f.environment.find("x"), genv);
+  }
+}
+
+g.f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-getVariable-14.js
@@ -0,0 +1,18 @@
+// Debugger.Environment can reflect optimized out function scopes
+
+var g = newGlobal();
+var dbg = new Debugger;
+dbg.addDebuggee(g);
+
+g.eval("" + function f() {
+  var x = 42;
+  function g() { }
+  g();
+});
+
+dbg.onEnterFrame = function (f) {
+  if (f.callee && (f.callee.name === "g"))
+    assertEq(f.environment.parent.getVariable("x").optimizedOut, true);
+}
+
+g.f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-names-03.js
@@ -0,0 +1,22 @@
+// Optimized out scopes should have working names().
+
+var g = newGlobal();
+var dbg = new Debugger;
+dbg.addDebuggee(g);
+
+g.eval("" + function f() {
+  var x = 42;
+  function g() { }
+  g();
+});
+
+dbg.onEnterFrame = function (f) {
+  if (f.callee && (f.callee.name === "g")) {
+    var names = f.environment.parent.names();
+    assertEq(names.indexOf("x") !== -1, true);
+    assertEq(names.indexOf("g") !== -1, true);
+    assertEq(names.length, 3); // x,g,arguments
+  }
+}
+
+g.f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-optimizedOut-01.js
@@ -0,0 +1,44 @@
+// Optimized out scopes should be considered optimizedOut.
+
+var g = newGlobal();
+var dbg = new Debugger;
+dbg.addDebuggee(g);
+
+g.eval("" + function f() {
+  var x = 42;
+  {
+    let y = 43;
+    (function () { })();
+  }
+});
+
+dbg.onEnterFrame = function (f) {
+  if (f.callee && (f.callee.name === undefined)) {
+    blockenv = f.environment.parent;
+    assertEq(blockenv.optimizedOut, true);
+    assertEq(blockenv.inspectable, true);
+    assertEq(blockenv.type, "declarative");
+    assertEq(blockenv.callee, null);
+    assertEq(blockenv.names().indexOf("y") !== -1, true);
+
+    funenv = blockenv.parent;
+    assertEq(funenv.optimizedOut, true);
+    assertEq(funenv.inspectable, true);
+    assertEq(funenv.type, "declarative");
+    assertEq(funenv.callee, f.older.callee);
+    assertEq(funenv.names().indexOf("x") !== -1, true);
+
+    globalenv = funenv.parent;
+    assertEq(globalenv.optimizedOut, false);
+    assertEq(globalenv.inspectable, true);
+    assertEq(globalenv.type, "object");
+    assertEq(globalenv.callee, null);
+
+    dbg.removeDebuggee(g);
+
+    assertEq(blockenv.inspectable, false);
+    assertEq(funenv.inspectable, false);
+  }
+}
+
+g.f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-setVariable-13.js
@@ -0,0 +1,20 @@
+// Debugger.Environment should throw trying to setVariable on optimized out scope.
+
+load(libdir + "asserts.js");
+
+var g = newGlobal();
+var dbg = new Debugger;
+dbg.addDebuggee(g);
+
+g.eval("" + function f() {
+  var x = 42;
+  function g() { }
+  g();
+});
+
+dbg.onEnterFrame = function (f) {
+  if (f.callee && (f.callee.name === "g"))
+    assertThrowsInstanceOf(function () { f.environment.parent.setVariable("x", 43) }, ReferenceError);
+}
+
+g.f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1121083.js
@@ -0,0 +1,16 @@
+// |jit-test| error:terminated
+options('werror');
+
+g = newGlobal();
+g.parent = this;
+g.eval("Debugger(parent).onExceptionUnwind = function () {};");
+
+function f(x) {
+    if (x === 0) {
+        return;
+    }
+    f(x - 1);
+    f(x - 1);
+}
+timeout(0.00001);
+f(100);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -410,20 +410,16 @@ BaselineCompiler::emitPrologue()
         return false;
 
     if (!emitArgumentTypeChecks())
         return false;
 
     if (!emitSPSPush())
         return false;
 
-    // Pad a nop so that the last non-op ICEntry we pushed does not get
-    // confused with the start address of the first op for PC mapping.
-    masm.nop();
-
     return true;
 }
 
 bool
 BaselineCompiler::emitEpilogue()
 {
     // Record the offset of the epilogue, so we can do early return from
     // Debugger handlers during on-stack recompile.
@@ -2998,48 +2994,33 @@ BaselineCompiler::emit_JSOP_PUSHBLOCKSCO
 }
 
 typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *);
 static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
 
 bool
 BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
 {
-#ifdef DEBUG
-    // The static block scope ends right before this op. Assert we generated
-    // JIT code for the previous op, so that pcForNativeOffset does not
-    // incorrectly return this pc instead of the previous one and confuse
-    // ScopeIter::settle. TODO: remove this when bug 1118826 lands.
-    PCMappingEntry &prevEntry = pcMappingEntries_[pcMappingEntries_.length() - 2];
-    PCMappingEntry &curEntry = pcMappingEntries_[pcMappingEntries_.length() - 1];
-    MOZ_ASSERT(curEntry.pcOffset == script->pcToOffset(pc));
-    MOZ_ASSERT(curEntry.nativeOffset > prevEntry.nativeOffset);
-#endif
-
     // Call a stub to pop the block from the block chain.
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
     return callVM(PopBlockScopeInfo);
 }
 
 typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
 static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock);
 
 bool
 BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
 {
-    if (!compileDebugInstrumentation_) {
-        // See the comment in emit_JSOP_POPBLOCKSCOPE.
-        if (*GetNextPc(pc) == JSOP_POPBLOCKSCOPE)
-            masm.nop();
+    if (!compileDebugInstrumentation_)
         return true;
-    }
 
     prepareVMCall();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(ImmPtr(pc));
     pushArg(R0.scratchReg());
 
     return callVM(DebugLeaveBlockInfo);
 }
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -200,23 +200,19 @@ CollectJitStackScripts(JSContext *cx, co
                 // use the BaselineDebugModeOSRInfo on the frame directly to
                 // patch. Indeed, we cannot use iter.returnAddressToFp(), as
                 // it points into the debug mode OSR handler and cannot be
                 // used to look up a corresponding ICEntry.
                 //
                 // See cases F and G in PatchBaselineFramesForDebugMode.
                 if (!entries.append(DebugModeOSREntry(script, info)))
                     return false;
-            } else if (frame->isDebuggerHandlingException() && frame->maybeOverridePc()) {
+            } else if (frame->isDebuggerHandlingException()) {
                 // We are in the middle of handling an exception and the frame
-                // has an override pc. This happens since we could have bailed
-                // out in place from Ion after a throw, settling on the pc which
-                // may have no ICEntry (e.g., Ion is free to insert resume
-                // points after non-effectful ops for better register
-                // allocation).
+                // must have an override pc.
                 uint32_t offset = script->pcToOffset(frame->overridePc());
                 if (!entries.append(DebugModeOSREntry(script, offset)))
                     return false;
             } else {
                 // The frame must be settled on a pc with an ICEntry.
                 uint8_t *retAddr = iter.returnAddressToFp();
                 ICEntry &icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
                 if (!entries.append(DebugModeOSREntry(script, icEntry)))
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -58,17 +58,17 @@ BaselineFrame::trace(JSTracer *trc, JitF
     JSScript *script = this->script();
     size_t nfixed = script->nfixed();
     size_t nlivefixed = script->nbodyfixed();
 
     if (nfixed != nlivefixed) {
         jsbytecode *pc;
         frameIterator.baselineScriptAndPc(nullptr, &pc);
 
-        NestedScopeObject *staticScope = script->getStaticScope(pc);
+        NestedScopeObject *staticScope = script->getStaticBlockScope(pc);
         while (staticScope && !staticScope->is<StaticBlockObject>())
             staticScope = staticScope->enclosingNestedScope();
 
         if (staticScope) {
             StaticBlockObject &blockObj = staticScope->as<StaticBlockObject>();
             nlivefixed = blockObj.localOffset() + blockObj.numVariables();
         }
     }
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -742,113 +742,75 @@ BaselineScript::nativeCodeForPC(JSScript
 
         curPC += GetBytecodeLength(curPC);
     }
 
     MOZ_CRASH("No native code for this pc");
 }
 
 jsbytecode *
-BaselineScript::pcForReturnOffset(JSScript *script, uint32_t nativeOffset)
-{
-    return pcForNativeOffset(script, nativeOffset, true);
-}
-
-jsbytecode *
-BaselineScript::pcForReturnAddress(JSScript *script, uint8_t *nativeAddress)
+BaselineScript::approximatePcForNativeAddress(JSScript *script, uint8_t *nativeAddress)
 {
     MOZ_ASSERT(script->baselineScript() == this);
     MOZ_ASSERT(nativeAddress >= method_->raw());
     MOZ_ASSERT(nativeAddress < method_->raw() + method_->instructionsSize());
-    return pcForReturnOffset(script, uint32_t(nativeAddress - method_->raw()));
-}
 
-jsbytecode *
-BaselineScript::pcForNativeOffset(JSScript *script, uint32_t nativeOffset)
-{
-    return pcForNativeOffset(script, nativeOffset, false);
-}
-
-jsbytecode *
-BaselineScript::pcForNativeOffset(JSScript *script, uint32_t nativeOffset, bool isReturn)
-{
-    MOZ_ASSERT(script->baselineScript() == this);
+    uint32_t nativeOffset = nativeAddress - method_->raw();
     MOZ_ASSERT(nativeOffset < method_->instructionsSize());
 
     // Look for the first PCMappingIndexEntry with native offset > the native offset we are
     // interested in.
     uint32_t i = 1;
     for (; i < numPCMappingIndexEntries(); i++) {
         if (pcMappingIndexEntry(i).nativeOffset > nativeOffset)
             break;
     }
 
     // Go back an entry to search forward from.
     MOZ_ASSERT(i > 0);
     i--;
 
     PCMappingIndexEntry &entry = pcMappingIndexEntry(i);
-    MOZ_ASSERT_IF(isReturn, nativeOffset >= entry.nativeOffset);
 
     CompactBufferReader reader(pcMappingReader(i));
     jsbytecode *curPC = script->offsetToPC(entry.pcOffset);
     uint32_t curNativeOffset = entry.nativeOffset;
 
     MOZ_ASSERT(script->containsPC(curPC));
-    MOZ_ASSERT_IF(isReturn, nativeOffset >= curNativeOffset);
 
-    // In the raw native-lookup case, the native code address can occur
-    // before the start of ops.  Associate those with bytecode offset 0.
-    if (!isReturn && (curNativeOffset > nativeOffset))
+    // The native code address can occur before the start of ops.
+    // Associate those with bytecode offset 0.
+    if (curNativeOffset > nativeOffset)
         return script->code();
 
-    mozilla::DebugOnly<uint32_t> lastNativeOffset = curNativeOffset;
     jsbytecode *lastPC = curPC;
     while (true) {
         // If the high bit is set, the native offset relative to the
         // previous pc != 0 and comes next.
         uint8_t b = reader.readByte();
         if (b & 0x80)
             curNativeOffset += reader.readUnsigned();
 
         // Return the last PC that matched nativeOffset. Some bytecode
         // generate no native code (e.g., constant-pushing bytecode like
         // JSOP_INT8), and so their entries share the same nativeOffset as the
-        // next op that does generate code. Trying to find an entry for a
-        // return address is impossible for bytecodes that generate no code
-        // since calling this method requires VM reentry, so assert an exact
-        // match.
-        if (curNativeOffset > nativeOffset) {
-            MOZ_ASSERT_IF(isReturn, lastNativeOffset == nativeOffset);
+        // next op that does generate code.
+        if (curNativeOffset > nativeOffset)
             return lastPC;
-        }
 
-        // If this is a raw native lookup (not jsop return addresses), then
-        // the native address may lie in-between the last delta-entry in
+        // The native address may lie in-between the last delta-entry in
         // a pcMappingIndexEntry, and the next pcMappingIndexEntry.
-        if (!reader.more()) {
-            MOZ_ASSERT_IF(isReturn, curNativeOffset == nativeOffset);
+        if (!reader.more())
             return curPC;
-        }
 
-        lastNativeOffset = curNativeOffset;
         lastPC = curPC;
         curPC += GetBytecodeLength(curPC);
     }
 }
 
-jsbytecode *
-BaselineScript::pcForNativeAddress(JSScript *script, uint8_t *nativeAddress)
-{
-    MOZ_ASSERT(script->baselineScript() == this);
-    MOZ_ASSERT(nativeAddress >= method_->raw());
-    MOZ_ASSERT(nativeAddress < method_->raw() + method_->instructionsSize());
-    return pcForNativeOffset(script, uint32_t(nativeAddress - method_->raw()));
-}
-
 void
 BaselineScript::toggleDebugTraps(JSScript *script, jsbytecode *pc)
 {
     MOZ_ASSERT(script->baselineScript() == this);
 
     // Only scripts compiled for debug mode have toggled calls.
     if (!hasDebugInstrumentation())
         return;
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -366,30 +366,25 @@ struct BaselineScript
     }
 
     void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries);
     void copyPCMappingEntries(const CompactBufferWriter &entries);
 
     uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc,
                              PCMappingSlotInfo *slotInfo = nullptr);
 
-    jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
-    jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
-
-    jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
-    jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
+    // Return the bytecode offset for a given native code address. Be careful
+    // when using this method: we don't emit code for some bytecode ops, so
+    // the result may not be accurate.
+    jsbytecode *approximatePcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
 
     bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
     void unlinkDependentAsmJSModules(FreeOp *fop);
     void removeDependentAsmJSModule(DependentAsmJSModuleExit exit);
 
-  private:
-    jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset, bool isReturn);
-
-  public:
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
 #ifdef JS_TRACE_LOGGING
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -173,17 +173,17 @@ class CompileInfo
         // function to ensure that we do not try to embed a nursery pointer in
         // jit-code. Precisely because it can flow in from anywhere, it's not
         // guaranteed to be non-lazy. Hence, don't access its script!
         if (fun_) {
             fun_ = fun_->nonLazyScript()->functionNonDelazifying();
             MOZ_ASSERT(fun_->isTenured());
         }
 
-        osrStaticScope_ = osrPc ? script->getStaticScope(osrPc) : nullptr;
+        osrStaticScope_ = osrPc ? script->getStaticBlockScope(osrPc) : nullptr;
 
         nimplicit_ = StartArgSlot(script)                   /* scope chain and argument obj */
                    + (fun ? 1 : 0);                         /* this */
         nargs_ = fun ? fun->nargs() : 0;
         nbodyfixed_ = script->nbodyfixed();
         nlocals_ = script->nfixed();
         fixedLexicalBegin_ = script->fixedLexicalBegin();
         nstack_ = script->nslots() - script->nfixed();
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -514,20 +514,21 @@ HandleClosingGeneratorReturn(JSContext *
     }
 
     ForcedReturn(cx, frame, pc, rfe, calledDebugEpilogue);
 }
 
 struct AutoDebuggerHandlingException
 {
     BaselineFrame *frame;
-    explicit AutoDebuggerHandlingException(BaselineFrame *frame)
+    AutoDebuggerHandlingException(BaselineFrame *frame, jsbytecode *pc)
       : frame(frame)
     {
         frame->setIsDebuggerHandlingException();
+        frame->setOverridePc(pc); // Will be cleared in HandleException.
     }
     ~AutoDebuggerHandlingException() {
         frame->unsetIsDebuggerHandlingException();
     }
 };
 
 static void
 HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
@@ -549,17 +550,17 @@ HandleExceptionBaseline(JSContext *cx, c
     }
 
     RootedValue exception(cx);
     if (cx->isExceptionPending() && cx->compartment()->isDebuggee() &&
         cx->getPendingException(&exception) && !exception.isMagic(JS_GENERATOR_CLOSING))
     {
         // Set for debug mode OSR. See note concerning
         // 'isDebuggerHandlingException' in CollectJitStackScripts.
-        AutoDebuggerHandlingException debuggerHandling(frame.baselineFrame());
+        AutoDebuggerHandlingException debuggerHandling(frame.baselineFrame(), pc);
 
         switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) {
           case JSTRAP_ERROR:
             // Uncatchable exception.
             MOZ_ASSERT(!cx->isExceptionPending());
             break;
 
           case JSTRAP_CONTINUE:
@@ -580,17 +581,17 @@ HandleExceptionBaseline(JSContext *cx, c
         HandleClosingGeneratorReturn(cx, frame, pc, *unwoundScopeToPc, rfe, calledDebugEpilogue);
         return;
     }
 
     JSTryNote *tn = script->trynotes()->vector;
     JSTryNote *tnEnd = tn + script->trynotes()->length;
 
     uint32_t pcOffset = uint32_t(pc - script->main());
-    ScopeIter si(frame.baselineFrame(), pc, cx);
+    ScopeIter si(cx, frame.baselineFrame(), pc);
     for (; tn != tnEnd; ++tn) {
         if (pcOffset < tn->start)
             continue;
         if (pcOffset >= tn->start + tn->length)
             continue;
 
         // Skip if the try note's stack depth exceeds the frame's stack depth.
         // See the big comment in TryNoteIter::settle for more info.
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -73,17 +73,18 @@ JitcodeGlobalEntry::IonEntry::destroy()
 bool
 JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime *rt, void *ptr,
                                                    BytecodeLocationVector &results,
                                                    uint32_t *depth) const
 {
     MOZ_ASSERT(containsPointer(ptr));
     MOZ_ASSERT(script_->hasBaselineScript());
 
-    jsbytecode *pc = script_->baselineScript()->pcForNativeAddress(script_, (uint8_t*) ptr);
+    uint8_t *addr = reinterpret_cast<uint8_t*>(ptr);
+    jsbytecode *pc = script_->baselineScript()->approximatePcForNativeAddress(script_, addr);
     if (!results.append(BytecodeLocation(script_, pc)))
         return false;
 
     *depth = 1;
 
     return true;
 }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -780,18 +780,18 @@ DebugEpilogueOnBaselineReturn(JSContext 
 
     return true;
 }
 
 bool
 DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
 {
     // Unwind scope chain to stack depth 0.
-    ScopeIter si(frame, pc, cx);
-    UnwindAllScopes(cx, si);
+    ScopeIter si(cx, frame, pc);
+    UnwindAllScopesInFrame(cx, si);
     jsbytecode *unwindPc = frame->script()->main();
     frame->setOverridePc(unwindPc);
 
     // If Debugger::onLeaveFrame returns |true| we have to return the frame's
     // return value. If it returns |false|, the debugger threw an exception.
     // In both cases we have to pop debug scopes.
     ok = Debugger::onLeaveFrame(cx, frame, ok);
 
--- a/js/src/jit/shared/Assembler-x86-shared.cpp
+++ b/js/src/jit/shared/Assembler-x86-shared.cpp
@@ -137,17 +137,17 @@ AssemblerX86Shared::InvertCondition(Cond
       default:
         MOZ_CRASH("unexpected condition");
     }
 }
 
 CPUInfo::SSEVersion CPUInfo::maxSSEVersion = UnknownSSE;
 CPUInfo::SSEVersion CPUInfo::maxEnabledSSEVersion = UnknownSSE;
 bool CPUInfo::avxPresent = false;
-bool CPUInfo::avxEnabled = true;
+bool CPUInfo::avxEnabled = false;
 
 static uintptr_t
 ReadXGETBV()
 {
     // We use a variety of low-level mechanisms to get at the xgetbv
     // instruction, including spelling out the xgetbv instruction as bytes,
     // because older compilers and assemblers may not recognize the instruction
     // by name.
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -178,17 +178,17 @@ class CPUInfo
     static bool IsSSE41Present() { return GetSSEVersion() >= SSE4_1; }
     static bool IsSSE42Present() { return GetSSEVersion() >= SSE4_2; }
 
 #ifdef JS_CODEGEN_X86
     static void SetFloatingPointDisabled() { maxEnabledSSEVersion = NoSSE; avxEnabled = false; }
 #endif
     static void SetSSE3Disabled() { maxEnabledSSEVersion = SSE2; avxEnabled = false; }
     static void SetSSE4Disabled() { maxEnabledSSEVersion = SSSE3; avxEnabled = false; }
-    static void SetAVXDisabled() { avxEnabled = false; }
+    static void SetAVXEnabled() { avxEnabled = true; }
 };
 
 class AssemblerX86Shared : public AssemblerShared
 {
   protected:
     struct RelativePatch {
         int32_t offset;
         void *target;
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -381,16 +381,17 @@ MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT,     0
 MSG_DEF(JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED, 0, JSEXN_TYPEERR, "resumption values are disallowed in this hook")
 MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0, JSEXN_TYPEERR, "variable not found in environment")
 MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY,    3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
 MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 0, JSEXN_TYPEERR, "value is not a function or undefined")
 MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
 MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
 MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object with 'innermost' property must have 'line' and either 'displayURL', 'url', or 'source'")
 MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'displayURL', 'url', or 'source' property")
+MSG_DEF(JSMSG_DEBUG_CANT_SET_OPT_ENV, 1, JSEXN_REFERENCEERR, "can't set `{0}' in an optimized-out environment")
 
 // Intl
 MSG_DEF(JSMSG_DATE_NOT_FINITE,         0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_INTERNAL_INTL_ERROR,     0, JSEXN_ERR, "internal error while computing Intl data")
 MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED,  3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
 MSG_DEF(JSMSG_INTL_OBJECT_REINITED,    0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
 MSG_DEF(JSMSG_INVALID_CURRENCY_CODE,   1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
 MSG_DEF(JSMSG_INVALID_DIGITS_VALUE,    1, JSEXN_RANGEERR, "invalid digits value: {0}")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3471,16 +3471,35 @@ CreateScopeObjectsForScopeChain(JSContex
         dynamicEnclosingScope = dynamicWith;
     }
 
     dynamicScopeObj.set(dynamicEnclosingScope);
     staticScopeObj.set(staticEnclosingScope);
     return true;
 }
 
+static bool
+IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
+{
+    if (!fun->isInterpreted())
+        return true;
+
+    // If a function was compiled to be lexically nested inside some other
+    // script, we cannot clone it without breaking the compiler's assumptions.
+    JSObject *scope = fun->nonLazyScript()->enclosingStaticScope();
+    if (scope && (!scope->is<StaticEvalObject>() ||
+                  scope->as<StaticEvalObject>().isDirect() ||
+                  scope->as<StaticEvalObject>().isStrict()))
+    {
+        return false;
+    }
+
+    return !fun->nonLazyScript()->compileAndGo() || dynamicScope->is<GlobalObject>();
+}
+
 static JSObject *
 CloneFunctionObject(JSContext *cx, HandleObject funobj, HandleObject dynamicScope)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, dynamicScope);
     MOZ_ASSERT(dynamicScope);
     // Note that funobj can be in a different compartment.
@@ -3493,23 +3512,18 @@ CloneFunctionObject(JSContext *cx, Handl
     }
 
     RootedFunction fun(cx, &funobj->as<JSFunction>());
     if (fun->isInterpretedLazy()) {
         AutoCompartment ac(cx, funobj);
         if (!fun->getOrCreateScript(cx))
             return nullptr;
     }
-    /*
-     * If a function was compiled to be lexically nested inside some other
-     * script, we cannot clone it without breaking the compiler's assumptions.
-     */
-    if (fun->isInterpreted() && (fun->nonLazyScript()->enclosingStaticScope() ||
-        (fun->nonLazyScript()->compileAndGo() && !dynamicScope->is<GlobalObject>())))
-    {
+
+    if (!IsFunctionCloneable(fun, dynamicScope)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
         return nullptr;
     }
 
     if (fun->isBoundFunction()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
@@ -4014,17 +4028,18 @@ JS::Compile(JSContext *cx, HandleObject 
             SourceBufferHolder &srcBuf, MutableHandleScript script)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     AutoLastFrameCheck lfc(cx);
 
-    script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), options, srcBuf));
+    script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), NullPtr(),
+                                       options, srcBuf));
     return !!script;
 }
 
 bool
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
             const char16_t *chars, size_t length, MutableHandleScript script)
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
@@ -4414,17 +4429,17 @@ Evaluate(JSContext *cx, HandleObject obj
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     AutoLastFrameCheck lfc(cx);
 
     options.setCompileAndGo(obj->is<GlobalObject>());
     SourceCompressionTask sct(cx);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                    obj, NullPtr(), options,
+                                                    obj, NullPtr(), NullPtr(), options,
                                                     srcBuf, nullptr, 0, &sct));
     if (!script)
         return false;
 
     MOZ_ASSERT(script->getVersion() == options.version);
 
     bool result = Execute(cx, script, *obj,
                           options.noScriptRval ? nullptr : rval.address());
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -392,17 +392,17 @@ js::GetOutermostEnclosingFunctionOfScrip
     if (iter.done())
         return nullptr;
 
     if (!iter.isFunctionFrame())
         return nullptr;
 
     RootedFunction curr(cx, iter.callee(cx));
     for (StaticScopeIter<NoGC> i(curr); !i.done(); i++) {
-        if (i.type() == StaticScopeIter<NoGC>::FUNCTION)
+        if (i.type() == StaticScopeIter<NoGC>::Function)
             curr = &i.fun();
     }
     return curr;
 }
 
 JS_FRIEND_API(JSFunction *)
 js::DefineFunctionWithReserved(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                                unsigned nargs, unsigned attrs)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3971,17 +3971,17 @@ js_DumpInterpreterFrame(JSContext *cx, I
         fputc('\n', stderr);
 
         fprintf(stderr, "file %s line %u\n",
                 i.script()->filename(), (unsigned) i.script()->lineno());
 
         if (jsbytecode *pc = i.pc()) {
             fprintf(stderr, "  pc = %p\n", pc);
             fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
-            MaybeDumpObject("staticScope", i.script()->getStaticScope(pc));
+            MaybeDumpObject("staticScope", i.script()->getStaticBlockScope(pc));
         }
         MaybeDumpValue("this", i.thisv(cx));
         if (!i.isJit()) {
             fprintf(stderr, "  rval: ");
             dumpValue(i.interpFrame()->returnValue());
             fputc('\n', stderr);
         }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1687,17 +1687,17 @@ ExpressionDecompiler::getLocal(uint32_t 
     if (local < script->nbodyfixed()) {
         for (BindingIter bi(script); bi; bi++) {
             if (bi->kind() != Binding::ARGUMENT && !bi->aliased() && bi.frameIndex() == local)
                 return bi->name();
         }
 
         MOZ_CRASH("No binding");
     }
-    for (NestedScopeObject *chain = script->getStaticScope(pc);
+    for (NestedScopeObject *chain = script->getStaticBlockScope(pc);
          chain;
          chain = chain->enclosingNestedScope())
     {
         if (!chain->is<StaticBlockObject>())
             continue;
         StaticBlockObject &block = chain->as<StaticBlockObject>();
         if (local < block.localOffset())
             continue;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -976,20 +976,21 @@ js::XDRScript(XDRState<mode> *xdr, Handl
                     funEnclosingScope = function->nonLazyScript()->enclosingStaticScope();
                 else {
                     MOZ_ASSERT(function->isAsmJSNative());
                     JS_ReportError(cx, "AsmJS modules are not yet supported in XDR serialization.");
                     return false;
                 }
 
                 StaticScopeIter<NoGC> ssi(funEnclosingScope);
-                if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::FUNCTION) {
+
+                if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::Function) {
                     MOZ_ASSERT(ssi.done() == !fun);
                     funEnclosingScopeIndex = UINT32_MAX;
-                } else if (ssi.type() == StaticScopeIter<NoGC>::BLOCK) {
+                } else if (ssi.type() == StaticScopeIter<NoGC>::Block) {
                     funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block());
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
                 } else {
                     funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith());
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
                 }
             }
 
@@ -3012,19 +3013,19 @@ js::CloneScript(JSContext *cx, HandleObj
                     if (innerFun->isInterpretedLazy()) {
                         AutoCompartment ac(cx, innerFun);
                         if (!innerFun->getOrCreateScript(cx))
                             return nullptr;
                     }
                     RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
                     StaticScopeIter<CanGC> ssi(cx, staticScope);
                     RootedObject enclosingScope(cx);
-                    if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::FUNCTION)
+                    if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::Function)
                         enclosingScope = fun;
-                    else if (ssi.type() == StaticScopeIter<CanGC>::BLOCK)
+                    else if (ssi.type() == StaticScopeIter<CanGC>::Block)
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
                     else
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
 
                     clone = CloneFunctionAndScript(cx, enclosingScope, innerFun);
                 }
             } else {
                 /*
@@ -3162,20 +3163,28 @@ bool
 js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone,
                         NewObjectKind newKind /* = GenericObject */)
 {
     MOZ_ASSERT(clone->isInterpreted());
 
     RootedScript script(cx, clone->nonLazyScript());
     MOZ_ASSERT(script);
     MOZ_ASSERT(script->compartment() == original->compartment());
-    MOZ_ASSERT_IF(script->compartment() != cx->compartment(),
-                  !script->enclosingStaticScope());
-
+
+    // The only scripts with enclosing static scopes that may be cloned across
+    // compartments are non-strict, indirect eval scripts, as their dynamic
+    // scope chains terminate in the global scope immediately.
     RootedObject scope(cx, script->enclosingStaticScope());
+    if (script->compartment() != cx->compartment() && scope) {
+        MOZ_ASSERT(!scope->as<StaticEvalObject>().isDirect() &&
+                   !scope->as<StaticEvalObject>().isStrict());
+        scope = StaticEvalObject::create(cx, NullPtr());
+        if (!scope)
+            return false;
+    }
 
     clone->mutableScript().init(nullptr);
 
     JSScript *cscript = CloneScript(cx, scope, clone, script, newKind);
     if (!cscript)
         return false;
 
     clone->setScript(cscript);
@@ -3464,17 +3473,17 @@ LazyScript::markChildren(JSTracer *trc)
 void
 LazyScript::finalize(FreeOp *fop)
 {
     if (table_)
         fop->free_(table_);
 }
 
 NestedScopeObject *
-JSScript::getStaticScope(jsbytecode *pc)
+JSScript::getStaticBlockScope(jsbytecode *pc)
 {
     MOZ_ASSERT(containsPC(pc));
 
     if (!hasBlockScopes())
         return nullptr;
 
     if (pc < main())
         return nullptr;
@@ -3518,16 +3527,32 @@ JSScript::getStaticScope(jsbytecode *pc)
         } else {
             top = mid;
         }
     }
 
     return blockChain;
 }
 
+JSObject *
+JSScript::innermostStaticScopeInScript(jsbytecode *pc)
+{
+    if (JSObject *scope = getStaticBlockScope(pc))
+        return scope;
+    return functionNonDelazifying();
+}
+
+JSObject *
+JSScript::innermostStaticScope(jsbytecode *pc)
+{
+    if (JSObject *scope = innermostStaticScopeInScript(pc))
+        return scope;
+    return enclosingStaticScope();
+}
+
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
     needsArgsAnalysis_ = true;
 }
 
 void
@@ -3832,17 +3857,17 @@ LazyScript::hasUncompiledEnclosingScript
     JSFunction &fun = enclosingScope()->as<JSFunction>();
     return fun.isInterpreted() && (!fun.mutableScript() || !fun.nonLazyScript()->code());
 }
 
 uint32_t
 LazyScript::staticLevel(JSContext *cx) const
 {
     for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) {
-        if (ssi.type() == StaticScopeIter<NoGC>::FUNCTION)
+        if (ssi.type() == StaticScopeIter<NoGC>::Function)
             return ssi.funScript()->staticLevel() + 1;
     }
     return 1;
 }
 
 void
 JSScript::updateBaselineOrIonRaw(JSContext *maybecx)
 {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1615,17 +1615,25 @@ class JSScript : public js::gc::TenuredC
     inline js::RegExpObject *getRegExp(jsbytecode *pc);
 
     const js::Value &getConst(size_t index) {
         js::ConstArray *arr = consts();
         MOZ_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
-    js::NestedScopeObject *getStaticScope(jsbytecode *pc);
+    js::NestedScopeObject *getStaticBlockScope(jsbytecode *pc);
+
+    // Returns the innermost static scope at pc if it falls within the extent
+    // of the script. Returns nullptr otherwise.
+    JSObject *innermostStaticScopeInScript(jsbytecode *pc);
+
+    // As innermostStaticScopeInScript, but returns the enclosing static scope
+    // if the innermost static scope falls without the extent of the script.
+    JSObject *innermostStaticScope(jsbytecode *pc);
 
     /*
      * The isEmpty method tells whether this script has code that computes any
      * result (not return value, result AKA normal completion value) other than
      * JSVAL_VOID, or any other effects.
      */
     bool isEmpty() const {
         if (length() > 3)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5894,18 +5894,19 @@ main(int argc, char **argv, char **envp)
                             "Wait for COUNT calls or iterations before baseline-compiling "
                             "(default: 10)", -1)
         || !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations "
                              "to test JIT codegen (no-op on platforms other than x86).")
         || !op.addBoolOption('\0', "no-sse3", "Pretend CPU does not support SSE3 instructions and above "
                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
         || !op.addBoolOption('\0', "no-sse4", "Pretend CPU does not support SSE4 instructions"
                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
-        || !op.addBoolOption('\0', "no-avx", "Pretend CPU does not support AVX instructions"
-                             "to test JIT codegen (no-op on platforms other than x86 and x64).")
+        || !op.addBoolOption('\0', "enable-avx", "AVX is disabled by default. Enable AVX. "
+                             "(no-op on platforms other than x86 and x64).")
+        || !op.addBoolOption('\0', "no-avx", "No-op. AVX is currently disabled by default.")
         || !op.addBoolOption('\0', "fuzzing-safe", "Don't expose functions that aren't safe for "
                              "fuzzers to call")
         || !op.addBoolOption('\0', "no-threads", "Disable helper threads and PJS threads")
 #ifdef DEBUG
         || !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are "
                              "unnecessarily entrained by inner functions")
 #endif
         || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC")
@@ -5977,19 +5978,19 @@ main(int argc, char **argv, char **envp)
     if (op.getBoolOption("no-sse3")) {
         js::jit::CPUInfo::SetSSE3Disabled();
         PropagateFlagToNestedShells("--no-sse3");
     }
     if (op.getBoolOption("no-sse4")) {
         js::jit::CPUInfo::SetSSE4Disabled();
         PropagateFlagToNestedShells("--no-sse4");
     }
-    if (op.getBoolOption("no-avx")) {
-        js::jit::CPUInfo::SetAVXDisabled();
-        PropagateFlagToNestedShells("--no-avx");
+    if (op.getBoolOption("enable-avx")) {
+        js::jit::CPUInfo::SetAVXEnabled();
+        PropagateFlagToNestedShells("--enable-avx");
     }
 #endif
 
     if (op.getBoolOption("no-threads"))
         js::DisableExtraThreads();
 
     // Start the engine.
     if (!JS_Init())
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5904,42 +5904,47 @@ DebuggerFrame_setOnPop(JSContext *cx, un
  *
  * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
  * must be either |frame|'s DebugScopeObject, or some extension of that
  * environment; either way, |frame|'s scope is where newly declared variables
  * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
  */
 bool
 js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
-                  mozilla::Range<const char16_t> chars, const char *filename, unsigned lineno,
-                  MutableHandleValue rval)
+                  jsbytecode *pc, mozilla::Range<const char16_t> chars, const char *filename,
+                  unsigned lineno, MutableHandleValue rval)
 {
     assertSameCompartment(cx, env, frame);
     MOZ_ASSERT_IF(frame, thisv.get() == frame.thisValue());
+    MOZ_ASSERT_IF(frame, pc);
 
     MOZ_ASSERT(!IsPoisonedPtr(chars.start().get()));
 
     /*
      * NB: This function breaks the assumption that the compiler can see all
      * calls and properly compute a static level. In practice, any non-zero
      * static level will suffice.
+     *
+     * Pass in NullPtr for evalStaticScope, as ScopeIter should stop at any
+     * non-ScopeObject boundaries, and we are putting a DebugScopeProxy on the
+     * scope chain.
      */
     CompileOptions options(cx);
     options.setCompileAndGo(true)
            .setForEval(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
            .setCanLazilyParse(false)
            .setIntroductionType("debugger eval")
            .maybeMakeStrictMode(frame ? frame.script()->strict() : false);
     RootedScript callerScript(cx, frame ? frame.script() : nullptr);
     SourceBufferHolder srcBuf(chars.start().get(), chars.length(), SourceBufferHolder::NoOwnership);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, callerScript,
-                                                    options, srcBuf,
-                                                    /* source = */ nullptr,
+                                                    /* evalStaticScope = */ js::NullPtr(),
+                                                    options, srcBuf, /* source = */ nullptr,
                                                     /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
     script->setActiveEval();
     ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
     return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address());
 }
@@ -6067,22 +6072,23 @@ DebuggerGenericEval(JSContext *cx, const
             }
         }
         env = nenv;
     }
 
     /* Run the code and produce the completion value. */
     RootedValue rval(cx);
     AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
+    jsbytecode *pc = iter ? iter->pc() : nullptr;
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, flat))
         return false;
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
-    bool ok = EvaluateInEnv(cx, env, thisv, frame, chars, url ? url : "debugger eval code",
+    bool ok = EvaluateInEnv(cx, env, thisv, frame, pc, chars, url ? url : "debugger eval code",
                             lineNumber, &rval);
     return dbg->receiveCompletionValue(ac, ok, rval, vp);
 }
 
 static bool
 DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME_ITER(cx, argc, vp, "eval", args, thisobj, _, iter);
@@ -7249,16 +7255,32 @@ DebuggerEnv_getInspectable(JSContext *cx
 
     Debugger *dbg = Debugger::fromChildJSObject(envobj);
 
     args.rval().setBoolean(dbg->observesGlobal(&env->global()));
     return true;
 }
 
 static bool
+DebuggerEnv_getOptimizedOut(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    NativeObject *envobj = DebuggerEnv_checkThis(cx, args, "get optimizedOut", false);
+    if (!envobj)
+        return false;
+    Rooted<Env*> env(cx, static_cast<Env *>(envobj->getPrivate()));
+    MOZ_ASSERT(env);
+    MOZ_ASSERT(!env->is<ScopeObject>());
+
+    args.rval().setBoolean(env->is<DebugScopeObject>() &&
+                           env->as<DebugScopeObject>().isOptimizedOut());
+    return true;
+}
+
+static bool
 DebuggerEnv_names(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGENV(cx, argc, vp, "names", args, envobj, env);
 
     AutoIdVector keys(cx);
     {
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, env);
@@ -7393,16 +7415,17 @@ DebuggerEnv_setVariable(JSContext *cx, u
 }
 
 static const JSPropertySpec DebuggerEnv_properties[] = {
     JS_PSG("type", DebuggerEnv_getType, 0),
     JS_PSG("object", DebuggerEnv_getObject, 0),
     JS_PSG("parent", DebuggerEnv_getParent, 0),
     JS_PSG("callee", DebuggerEnv_getCallee, 0),
     JS_PSG("inspectable", DebuggerEnv_getInspectable, 0),
+    JS_PSG("optimizedOut", DebuggerEnv_getOptimizedOut, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec DebuggerEnv_methods[] = {
     JS_FN("names", DebuggerEnv_names, 0, 0),
     JS_FN("find", DebuggerEnv_find, 1, 0),
     JS_FN("getVariable", DebuggerEnv_getVariable, 1, 0),
     JS_FN("setVariable", DebuggerEnv_setVariable, 2, 0),
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -907,16 +907,16 @@ Debugger::onLogAllocationSite(JSContext 
     GlobalObject::DebuggerVector *dbgs = cx->global()->getDebuggers();
     if (!dbgs || dbgs->empty())
         return true;
     return Debugger::slowPathOnLogAllocationSite(cx, frame, when, *dbgs);
 }
 
 extern bool
 EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
-              mozilla::Range<const char16_t> chars, const char *filename, unsigned lineno,
-              MutableHandleValue rval);
+              jsbytecode *pc, mozilla::Range<const char16_t> chars, const char *filename,
+              unsigned lineno, MutableHandleValue rval);
 
 bool ReportObjectRequired(JSContext *cx);
 
 } /* namespace js */
 
 #endif /* vm_Debugger_h */
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1185,17 +1185,17 @@ HelperThread::handleParseWorkload()
 
     {
         AutoUnlockHelperThreadState unlock;
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
         SourceBufferHolder srcBuf(parseTask->chars, parseTask->length,
                                   SourceBufferHolder::NoOwnership);
         parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
-                                                    NullPtr(), NullPtr(),
+                                                    NullPtr(), NullPtr(), NullPtr(),
                                                     parseTask->options,
                                                     srcBuf);
     }
 
     // The callback is invoked while we are still off the main thread.
     parseTask->callback(parseTask, parseTask->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -881,52 +881,51 @@ js::EnterWithOperation(JSContext *cx, Ab
 static void
 PopScope(JSContext *cx, ScopeIter &si)
 {
     switch (si.type()) {
       case ScopeIter::Block:
         if (cx->compartment()->isDebuggee())
             DebugScopes::onPopBlock(cx, si);
         if (si.staticBlock().needsClone())
-            si.frame().popBlock(cx);
+            si.initialFrame().popBlock(cx);
         break;
       case ScopeIter::With:
-        si.frame().popWith(cx);
+        si.initialFrame().popWith(cx);
         break;
       case ScopeIter::Call:
-      case ScopeIter::StrictEvalScope:
+      case ScopeIter::Eval:
         break;
     }
 }
 
 // Unwind scope chain and iterator to match the static scope corresponding to
 // the given bytecode position.
 void
 js::UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc)
 {
-    if (si.done())
+    if (!si.withinInitialFrame())
         return;
 
-    Rooted<NestedScopeObject *> staticScope(cx, si.frame().script()->getStaticScope(pc));
-
-    for (; si.staticScope() != staticScope; ++si)
+    RootedObject staticScope(cx, si.initialFrame().script()->innermostStaticScope(pc));
+    for (; si.maybeStaticScope() != staticScope; ++si)
         PopScope(cx, si);
 }
 
 // Unwind all scopes. This is needed because block scopes may cover the
 // first bytecode at a script's main(). e.g.,
 //
 //     function f() { { let i = 0; } }
 //
 // will have no pc location distinguishing the first block scope from the
 // outermost function scope.
 void
-js::UnwindAllScopes(JSContext *cx, ScopeIter &si)
+js::UnwindAllScopesInFrame(JSContext *cx, ScopeIter &si)
 {
-    for (; !si.done(); ++si)
+    for (; si.withinInitialFrame(); ++si)
         PopScope(cx, si);
 }
 
 // Compute the pc needed to unwind the scope to the beginning of a try
 // block. We cannot unwind to *after* the JSOP_TRY, because that might be the
 // first opcode of an inner scope, with the same problem as above. e.g.,
 //
 // try { { let x; } }
@@ -937,24 +936,24 @@ jsbytecode *
 js::UnwindScopeToTryPc(JSScript *script, JSTryNote *tn)
 {
     return script->main() + tn->start - js_CodeSpec[JSOP_TRY].length;
 }
 
 static void
 ForcedReturn(JSContext *cx, ScopeIter &si, InterpreterRegs &regs)
 {
-    UnwindAllScopes(cx, si);
+    UnwindAllScopesInFrame(cx, si);
     regs.setToEndOfScript();
 }
 
 static void
 ForcedReturn(JSContext *cx, InterpreterRegs &regs)
 {
-    ScopeIter si(regs.fp(), regs.pc, cx);
+    ScopeIter si(cx, regs.fp(), regs.pc);
     ForcedReturn(cx, si, regs);
 }
 
 void
 js::UnwindForUncatchableException(JSContext *cx, const InterpreterRegs &regs)
 {
     /* c.f. the regular (catchable) TryNoteIter loop in HandleError. */
     for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
@@ -1033,17 +1032,17 @@ enum HandleErrorContinuation
     FinallyContinuation
 };
 
 static HandleErrorContinuation
 HandleError(JSContext *cx, InterpreterRegs &regs)
 {
     MOZ_ASSERT(regs.fp()->script()->containsPC(regs.pc));
 
-    ScopeIter si(regs.fp(), regs.pc, cx);
+    ScopeIter si(cx, regs.fp(), regs.pc);
     bool ok = false;
 
   again:
     if (cx->isExceptionPending()) {
         /* Call debugger throw hooks. */
         RootedValue exception(cx);
         if (!cx->getPendingException(&exception))
             goto again;
@@ -3352,31 +3351,31 @@ CASE(JSOP_PUSHBLOCKSCOPE)
 }
 END_CASE(JSOP_PUSHBLOCKSCOPE)
 
 CASE(JSOP_POPBLOCKSCOPE)
 {
 #ifdef DEBUG
     // Pop block from scope chain.
     MOZ_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK);
-    NestedScopeObject *scope = script->getStaticScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
+    NestedScopeObject *scope = script->getStaticBlockScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
     MOZ_ASSERT(scope && scope->is<StaticBlockObject>());
     StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
     MOZ_ASSERT(blockObj.needsClone());
 #endif
 
     // Pop block from scope chain.
     REGS.fp()->popBlock(cx);
 }
 END_CASE(JSOP_POPBLOCKSCOPE)
 
 CASE(JSOP_DEBUGLEAVEBLOCK)
 {
-    MOZ_ASSERT(script->getStaticScope(REGS.pc));
-    MOZ_ASSERT(script->getStaticScope(REGS.pc)->is<StaticBlockObject>());
+    MOZ_ASSERT(script->getStaticBlockScope(REGS.pc));
+    MOZ_ASSERT(script->getStaticBlockScope(REGS.pc)->is<StaticBlockObject>());
 
     // FIXME: This opcode should not be necessary.  The debugger shouldn't need
     // help from bytecode to do its job.  See bug 927782.
 
     if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
         DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
 }
 END_CASE(JSOP_DEBUGLEAVEBLOCK)
@@ -4092,17 +4091,17 @@ js::ReportUninitializedLexical(JSContext
                 name = bi->name();
                 break;
             }
         }
 
         // Failing that, it must be a block-local let.
         if (!name) {
             // Skip to the right scope.
-            Rooted<NestedScopeObject *> scope(cx, script->getStaticScope(pc));
+            Rooted<NestedScopeObject *> scope(cx, script->getStaticBlockScope(pc));
             MOZ_ASSERT(scope && scope->is<StaticBlockObject>());
             Rooted<StaticBlockObject *> block(cx, &scope->as<StaticBlockObject>());
             while (slot < block->localOffset())
                 block = &block->enclosingNestedScope()->as<StaticBlockObject>();
 
             // Translate the frame slot to the block slot, then find the name
             // of the slot.
             uint32_t blockSlot = block->localIndexToSlot(slot);
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -241,17 +241,17 @@ HasInstance(JSContext *cx, HandleObject 
 
 // Unwind scope chain and iterator to match the static scope corresponding to
 // the given bytecode position.
 extern void
 UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc);
 
 // Unwind all scopes.
 extern void
-UnwindAllScopes(JSContext *cx, ScopeIter &si);
+UnwindAllScopesInFrame(JSContext *cx, ScopeIter &si);
 
 // Compute the pc needed to unwind the scope to the beginning of the block
 // pointed to by the try note.
 extern jsbytecode *
 UnwindScopeToTryPc(JSScript *script, JSTryNote *tn);
 
 /*
  * Unwind for an uncatchable exception. This means not running finalizers, etc;
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -81,79 +81,95 @@ StaticScopeIter<allowGC>::operator++(int
     if (obj->template is<NestedScopeObject>()) {
         obj = obj->template as<NestedScopeObject>().enclosingScopeForStaticScopeIter();
     } else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
         onNamedLambda = false;
         obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope();
     } else {
         onNamedLambda = true;
     }
-    MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() || obj->template is<JSFunction>());
+    MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() ||
+                       obj->template is<StaticEvalObject>() ||
+                       obj->template is<JSFunction>());
     MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
 }
 
 template <AllowGC allowGC>
 inline bool
 StaticScopeIter<allowGC>::hasDynamicScopeObject() const
 {
     return obj->template is<StaticBlockObject>()
            ? obj->template as<StaticBlockObject>().needsClone()
-           : (obj->template is<StaticWithObject>() ||
-              obj->template as<JSFunction>().isHeavyweight());
+           : (obj->template is<StaticEvalObject>()
+              ? obj->template as<StaticEvalObject>().isStrict()
+              : (obj->template is<StaticWithObject>() ||
+                 obj->template as<JSFunction>().isHeavyweight()));
 }
 
 template <AllowGC allowGC>
 inline Shape *
 StaticScopeIter<allowGC>::scopeShape() const
 {
     MOZ_ASSERT(hasDynamicScopeObject());
-    MOZ_ASSERT(type() != NAMED_LAMBDA);
-    if (type() == BLOCK)
+    MOZ_ASSERT(type() != NamedLambda && type() != Eval);
+    if (type() == Block)
         return block().lastProperty();
     return funScript()->callObjShape();
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
 StaticScopeIter<allowGC>::type() const
 {
     if (onNamedLambda)
-        return NAMED_LAMBDA;
+        return NamedLambda;
     return obj->template is<StaticBlockObject>()
-           ? BLOCK
-           : (obj->template is<StaticWithObject>() ? WITH : FUNCTION);
+           ? Block
+           : (obj->template is<StaticWithObject>()
+              ? With
+              : (obj->template is<StaticEvalObject>()
+                 ? Eval
+                 : Function));
 }
 
 template <AllowGC allowGC>
 inline StaticBlockObject &
 StaticScopeIter<allowGC>::block() const
 {
-    MOZ_ASSERT(type() == BLOCK);
+    MOZ_ASSERT(type() == Block);
     return obj->template as<StaticBlockObject>();
 }
 
 template <AllowGC allowGC>
 inline StaticWithObject &
 StaticScopeIter<allowGC>::staticWith() const
 {
-    MOZ_ASSERT(type() == WITH);
+    MOZ_ASSERT(type() == With);
     return obj->template as<StaticWithObject>();
 }
 
 template <AllowGC allowGC>
+inline StaticEvalObject &
+StaticScopeIter<allowGC>::eval() const
+{
+    MOZ_ASSERT(type() == Eval);
+    return obj->template as<StaticEvalObject>();
+}
+
+template <AllowGC allowGC>
 inline JSScript *
 StaticScopeIter<allowGC>::funScript() const
 {
-    MOZ_ASSERT(type() == FUNCTION);
+    MOZ_ASSERT(type() == Function);
     return obj->template as<JSFunction>().nonLazyScript();
 }
 
 template <AllowGC allowGC>
 inline JSFunction &
 StaticScopeIter<allowGC>::fun() const
 {
-    MOZ_ASSERT(type() == FUNCTION);
+    MOZ_ASSERT(type() == Function);
     return obj->template as<JSFunction>();
 }
 
 }  /* namespace js */
 
 #endif /* vm_ScopeObject_inl_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -29,32 +29,21 @@ using namespace js::types;
 
 using mozilla::PodZero;
 
 typedef Rooted<ArgumentsObject *> RootedArgumentsObject;
 typedef MutableHandle<ArgumentsObject *> MutableHandleArgumentsObject;
 
 /*****************************************************************************/
 
-static JSObject *
-InnermostStaticScope(JSScript *script, jsbytecode *pc)
-{
-    MOZ_ASSERT(script->containsPC(pc));
-    MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
-
-    NestedScopeObject *scope = script->getStaticScope(pc);
-    if (scope)
-        return scope;
-    return script->functionNonDelazifying();
-}
-
 Shape *
 js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc)
 {
-    StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
+    MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
+    StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
     uint32_t hops = ScopeCoordinate(pc).hops();
     while (true) {
         MOZ_ASSERT(!ssi.done());
         if (ssi.hasDynamicScopeObject()) {
             if (!hops)
                 break;
             hops--;
         }
@@ -108,27 +97,28 @@ js::ScopeCoordinateName(ScopeCoordinateN
     if (!JSID_IS_ATOM(id))
         return script->runtimeFromAnyThread()->commonNames->empty;
     return JSID_TO_ATOM(id)->asPropertyName();
 }
 
 JSScript *
 js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc)
 {
-    StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
+    MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
+    StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
     uint32_t hops = ScopeCoordinate(pc).hops();
     while (true) {
         if (ssi.hasDynamicScopeObject()) {
             if (!hops)
                 break;
             hops--;
         }
         ssi++;
     }
-    if (ssi.type() != StaticScopeIter<NoGC>::FUNCTION)
+    if (ssi.type() != StaticScopeIter<NoGC>::Function)
         return nullptr;
     return ssi.funScript();
 }
 
 /*****************************************************************************/
 
 void
 ScopeObject::setEnclosingScope(HandleObject obj)
@@ -284,16 +274,42 @@ CallObject::createForStrictEval(JSContex
     MOZ_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterRegs().pc == frame.script()->code());
 
     RootedFunction callee(cx);
     RootedScript script(cx, frame.script());
     RootedObject scopeChain(cx, frame.scopeChain());
     return create(cx, script, scopeChain, callee);
 }
 
+CallObject *
+CallObject::createHollowForDebug(JSContext *cx, HandleFunction callee)
+{
+    MOZ_ASSERT(!callee->isHeavyweight());
+
+    // This scope's parent link is never used: the DebugScopeObject that
+    // refers to this scope carries its own parent link, which is what
+    // Debugger uses to construct the tree of Debugger.Environment objects. So
+    // just parent this scope directly to the global.
+    Rooted<GlobalObject *> global(cx, &callee->global());
+    Rooted<CallObject *> callobj(cx, createForFunction(cx, global, callee));
+    if (!callobj)
+        return nullptr;
+
+    RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
+    RootedId id(cx);
+    RootedScript script(cx, callee->nonLazyScript());
+    for (BindingIter bi(script); !bi.done(); bi++) {
+        id = NameToId(bi->name());
+        if (!JSObject::setGeneric(cx, callobj, callobj, id, &optimizedOut, true))
+            return nullptr;
+    }
+
+    return callobj;
+}
+
 const Class CallObject::class_ = {
     "Call",
     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)
 };
 
 const Class DeclEnvObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
@@ -616,53 +632,109 @@ const Class DynamicWithObject::class_ = 
         with_DeleteGeneric,
         nullptr, nullptr,    /* watch/unwatch */
         nullptr,             /* getElements */
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
         with_ThisObject,
     }
 };
 
+/* static */ StaticEvalObject *
+StaticEvalObject::create(JSContext *cx, HandleObject enclosing)
+{
+    RootedTypeObject type(cx, cx->getNewType(&class_, TaggedProto(nullptr)));
+    if (!type)
+        return nullptr;
+
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
+                                                      cx->global(), nullptr, FINALIZE_KIND,
+                                                      BaseShape::DELEGATE));
+    if (!shape)
+        return nullptr;
+
+    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
+                                                                  gc::TenuredHeap, shape, type)));
+    if (!obj)
+        return nullptr;
+
+    obj->as<StaticEvalObject>().initEnclosingNestedScope(enclosing);
+    obj->setFixedSlot(STRICT_SLOT, BooleanValue(false));
+    return &obj->as<StaticEvalObject>();
+}
+
+const Class StaticEvalObject::class_ = {
+    "StaticEval",
+    JSCLASS_HAS_RESERVED_SLOTS(StaticEvalObject::RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS
+};
+
 /*****************************************************************************/
 
-ClonedBlockObject *
-ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame)
+/* static */ ClonedBlockObject *
+ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, HandleObject enclosing)
 {
-    assertSameCompartment(cx, frame);
     MOZ_ASSERT(block->getClass() == &BlockObject::class_);
 
     RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, TaggedProto(block.get())));
     if (!type)
         return nullptr;
 
     RootedShape shape(cx, block->lastProperty());
 
     RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
                                                                   gc::TenuredHeap, shape, type)));
     if (!obj)
         return nullptr;
 
     /* Set the parent if necessary, as for call objects. */
-    if (&frame.scopeChain()->global() != obj->getParent()) {
+    if (&enclosing->global() != obj->getParent()) {
         MOZ_ASSERT(obj->getParent() == nullptr);
-        Rooted<GlobalObject*> global(cx, &frame.scopeChain()->global());
+        Rooted<GlobalObject*> global(cx, &enclosing->global());
         if (!JSObject::setParent(cx, obj, global))
             return nullptr;
     }
 
     MOZ_ASSERT(!obj->inDictionaryMode());
     MOZ_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS);
 
-    obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*frame.scopeChain()));
+    obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*enclosing));
 
     MOZ_ASSERT(obj->isDelegate());
 
     return &obj->as<ClonedBlockObject>();
 }
 
+/* static */ ClonedBlockObject *
+ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame)
+{
+    assertSameCompartment(cx, frame);
+    RootedObject enclosing(cx, frame.scopeChain());
+    return create(cx, block, enclosing);
+}
+
+/* static */ ClonedBlockObject *
+ClonedBlockObject::createHollowForDebug(JSContext *cx, Handle<StaticBlockObject *> block)
+{
+    MOZ_ASSERT(!block->needsClone());
+
+    // This scope's parent link is never used: the DebugScopeObject that
+    // refers to this scope carries its own parent link, which is what
+    // Debugger uses to construct the tree of Debugger.Environment objects. So
+    // just parent this scope directly to the global.
+    Rooted<GlobalObject *> global(cx, &block->global());
+    Rooted<ClonedBlockObject *> obj(cx, create(cx, block, global));
+    if (!obj)
+        return nullptr;
+
+    for (unsigned i = 0; i < block->numVariables(); i++)
+        obj->setVar(i, MagicValue(JS_OPTIMIZED_OUT), DONT_CHECK_ALIASING);
+
+    return obj;
+}
+
 void
 ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame)
 {
     StaticBlockObject &block = staticBlock();
     for (unsigned i = 0; i < numVariables(); ++i) {
         if (!block.isAliased(i)) {
             Value &val = frame.unaliasedLocal(block.blockIndexToLocalIndex(i));
             setVar(i, val, DONT_CHECK_ALIASING);
@@ -1059,224 +1131,191 @@ const Class UninitializedLexicalObject::
 // Any name atom for a function which will be added as a DeclEnv object to the
 // scope chain above call objects for fun.
 static inline JSAtom *
 CallObjectLambdaName(JSFunction &fun)
 {
     return fun.isNamedLambda() ? fun.atom() : nullptr;
 }
 
-ScopeIter::ScopeIter(const ScopeIter &si, JSContext *cx
+ScopeIter::ScopeIter(JSContext *cx, const ScopeIter &si
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : cx(cx),