Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 01 Apr 2016 11:49:37 +0200
changeset 291375 38fa3ece64fbd82cd88a0dc91c820b786cbcee4c
parent 291374 2fd5231609577f953a0e932ce297b0a8e109412a (current diff)
parent 291188 538d248fa252a4100082fd9bc3fdc08d322cda22 (diff)
child 291376 a70b50e2830f3316d5a1307bb9f8231ca88efc52
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone48.0a1
Merge mozilla-central to fx-team
ipc/chromium/src/chrome/common/ipc_channel_proxy.cc
ipc/chromium/src/chrome/common/ipc_channel_proxy.h
ipc/chromium/src/chrome/common/ipc_sync_channel.cc
ipc/chromium/src/chrome/common/ipc_sync_channel.h
ipc/chromium/src/chrome/common/ipc_sync_message.cc
ipc/chromium/src/chrome/common/ipc_sync_message.h
ipc/chromium/src/chrome/common/message_router.cc
ipc/chromium/src/chrome/common/message_router.h
layout/base/tests/chrome/test_transformed_scrolling_repaints.html
layout/base/tests/chrome/test_transformed_scrolling_repaints_2.html
layout/base/tests/chrome/test_transformed_scrolling_repaints_3.html
layout/base/tests/chrome/transformed_scrolling_repaints_3_window.html
mobile/android/base/java/org/mozilla/gecko/GeckoAccessibility.java
--- a/accessible/base/EventQueue.cpp
+++ b/accessible/base/EventQueue.cpp
@@ -259,20 +259,18 @@ EventQueue::CoalesceReorderEvents(AccEve
     //   then ignore thisEvent and its show and hide events
     //   otherwise ignore thisEvent but not its show and hide events
     Accessible* thisParent = thisEvent->mAccessible;
     while (thisParent && thisParent != mDocument) {
       if (thisParent->Parent() == aTailEvent->mAccessible) {
         AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
         uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent);
 
-        // Sometimes InvalidateChildren() and
-        // DocAccessible::CacheChildrenInSubtree() can conspire to reparent an
-        // accessible in this case no need for mutation events.  Se bug 883708
-        // for details.
+        // It can be either hide or show events which may occur because of
+        // accessible reparenting.
         if (eventType == nsIAccessibleEvent::EVENT_SHOW ||
             eventType == nsIAccessibleEvent::EVENT_HIDE) {
           AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
           thisReorder->DoNotEmitAll();
         } else {
           thisEvent->mEventRule = AccEvent::eDoNotEmit;
         }
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1876,17 +1876,24 @@ Accessible::AppendTextTo(nsAString& aTex
 
 void
 Accessible::Shutdown()
 {
   // Mark the accessible as defunct, invalidate the child count and pointers to
   // other accessibles, also make sure none of its children point to this parent
   mStateFlags |= eIsDefunct;
 
-  InvalidateChildren();
+  int32_t childCount = mChildren.Length();
+  for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
+    mChildren.ElementAt(childIdx)->UnbindFromParent();
+  }
+  mChildren.Clear();
+
+  mEmbeddedObjCollector = nullptr;
+
   if (mParent)
     mParent->RemoveChild(this);
 
   mContent = nullptr;
   mDoc = nullptr;
   if (SelectionMgr() && SelectionMgr()->AccessibleWithCaret(nullptr) == this)
     SelectionMgr()->ResetCaretOffset();
 }
@@ -2064,29 +2071,16 @@ Accessible::Language(nsAString& aLanguag
 
   nsCoreUtils::GetLanguageFor(mContent, nullptr, aLanguage);
   if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
     mDoc->DocumentNode()->GetHeaderData(nsGkAtoms::headerContentLanguage,
                                         aLanguage);
   }
 }
 
-void
-Accessible::InvalidateChildren()
-{
-  int32_t childCount = mChildren.Length();
-  for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
-    mChildren.ElementAt(childIdx)->UnbindFromParent();
-  }
-
-  mEmbeddedObjCollector = nullptr;
-  mChildren.Clear();
-  SetChildrenFlag(eChildrenUninitialized);
-}
-
 bool
 Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
 {
   if (!aChild)
     return false;
 
   if (aIndex == mChildren.Length()) {
     if (!mChildren.AppendElement(aChild))
@@ -2594,17 +2588,19 @@ Accessible::EnsureChildren()
 {
   NS_ASSERTION(!IsDefunct(), "Caching children for defunct accessible!");
 
   if (!IsChildrenFlag(eChildrenUninitialized))
     return;
 
   // State is embedded children until text leaf accessible is appended.
   SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
-  CacheChildren();
+  if (KidsFromDOM()) {
+    CacheChildren();
+  }
 }
 
 Accessible*
 Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
 {
   if (!mParent || mIndexInParent == -1) {
     if (aError)
       *aError = NS_ERROR_UNEXPECTED;
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -376,24 +376,16 @@ public:
     { mRoleMapEntry = aRoleMapEntry; }
 
   /**
    * Cache children if necessary.
    */
   void EnsureChildren();
 
   /**
-   * Set the child count to -1 (unknown) and null out cached child pointers.
-   * Should be called when accessible tree is changed because document has
-   * transformed. Note, if accessible cares about its parent relation chain
-   * itself should override this method to do nothing.
-   */
-  virtual void InvalidateChildren();
-
-  /**
    * Append/insert/remove a child. Return true if operation was successful.
    */
   bool AppendChild(Accessible* aChild)
     { return InsertChildAt(mChildren.Length(), aChild); }
   virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild);
 
   bool InsertAfter(Accessible* aNewChild, Accessible* aRefChild)
   {
@@ -936,17 +928,23 @@ public:
     else
       mStateFlags &= ~eRelocated;
   }
 
   /**
    * Return true if the accessible doesn't allow accessible children from XBL
    * anonymous subtree.
    */
-  bool NoXBLKids() { return mStateFlags & eNoXBLKids; }
+  bool NoXBLKids() const { return mStateFlags & eNoXBLKids; }
+
+  /**
+   * Return true if the accessible allows accessible children from subtree of
+   * a DOM element of this accessible.
+   */
+  bool KidsFromDOM() const { return !(mStateFlags & eNoKidsFromDOM); }
 
   /**
    * Return true if this accessible has a parent whose name depends on this
    * accessible.
    */
   bool HasNameDependentParent() const
     { return mContextFlags & eHasNameDependentParent; }
 
@@ -1035,18 +1033,19 @@ protected:
     eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
     eHasNumericValue = 1 << 4, // accessible has a numeric value
     eGroupInfoDirty = 1 << 5, // accessible needs to update group info
     eSubtreeMutating = 1 << 6, // subtree is being mutated
     eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
     eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
     eRelocated = 1 << 9, // accessible was moved in tree
     eNoXBLKids = 1 << 10, // accessible don't allows XBL children
+    eNoKidsFromDOM = 1 << 11, // accessible doesn't allow children from DOM
 
-    eLastStateFlag = eNoXBLKids
+    eLastStateFlag = eNoKidsFromDOM
   };
 
   /**
    * Flags used for contextual information about the accessible.
    */
   enum ContextFlags {
     eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible.
     eARIAHidden = 1 << 1,
@@ -1152,17 +1151,17 @@ protected:
   nsCOMPtr<nsIContent> mContent;
   DocAccessible* mDoc;
 
   RefPtr<Accessible> mParent;
   nsTArray<RefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kChildrenFlagsBits = 2;
-  static const uint8_t kStateFlagsBits = 11;
+  static const uint8_t kStateFlagsBits = 12;
   static const uint8_t kContextFlagsBits = 3;
   static const uint8_t kTypeBits = 6;
   static const uint8_t kGenericTypesBits = 15;
 
   /**
    * Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes.
    */
   uint32_t mChildrenFlags : kChildrenFlagsBits;
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -144,23 +144,16 @@ ApplicationAccessible::NativeRole()
 }
 
 uint64_t
 ApplicationAccessible::NativeState()
 {
   return 0;
 }
 
-void
-ApplicationAccessible::InvalidateChildren()
-{
-  // Do nothing because application children are kept updated by AppendChild()
-  // and RemoveChild() method calls.
-}
-
 KeyBinding
 ApplicationAccessible::AccessKey() const
 {
   return KeyBinding();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected methods
--- a/accessible/generic/ApplicationAccessible.h
+++ b/accessible/generic/ApplicationAccessible.h
@@ -47,18 +47,16 @@ public:
   virtual uint64_t State() override;
   virtual uint64_t NativeState() override;
   virtual Relation RelationByType(RelationType aType) override;
 
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild) override;
   virtual Accessible* FocusedChild() override;
 
-  virtual void InvalidateChildren() override;
-
   // ActionAccessible
   virtual KeyBinding AccessKey() const override;
 
   // ApplicationAccessible
   void AppName(nsAString& aName) const
   {
     nsAutoCString cname;
     mAppInfo->GetName(cname);
--- a/accessible/generic/BaseAccessibles.cpp
+++ b/accessible/generic/BaseAccessibles.cpp
@@ -19,16 +19,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // LeafAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 LeafAccessible::
   LeafAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
+  mStateFlags |= eNoKidsFromDOM;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(LeafAccessible, Accessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // LeafAccessible: Accessible public
 
 Accessible*
@@ -55,25 +56,16 @@ LeafAccessible::RemoveChild(Accessible* 
 
 bool
 LeafAccessible::IsAcceptableChild(nsIContent* aEl) const
 {
   // No children for leaf accessible.
   return false;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// LeafAccessible: Accessible private
-
-void
-LeafAccessible::CacheChildren()
-{
-  // No children for leaf accessible.
-}
-
 
 ////////////////////////////////////////////////////////////////////////////////
 // LinkableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS_INHERITED0(LinkableAccessible, AccessibleWrap)
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/generic/BaseAccessibles.h
+++ b/accessible/generic/BaseAccessibles.h
@@ -37,19 +37,16 @@ public:
                                    EWhichChildAtPoint aWhichChild) override;
   virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override final;
   virtual bool RemoveChild(Accessible* aChild) override final;
 
   virtual bool IsAcceptableChild(nsIContent* aEl) const override;
 
 protected:
   virtual ~LeafAccessible() {}
-
-  // Accessible
-  virtual void CacheChildren() override;
 };
 
 /**
  * Used for text or image accessible nodes contained by link accessibles or
  * accessibles for nodes with registered click event handler. It knows how to
  * report the state of the host link (traveled or not) and can activate (click)
  * the host accessible programmatically.
  */
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1874,74 +1874,16 @@ DocAccessible::FireEventsOnInsertion(Acc
     while ((ancestor = ancestor->Parent()));
   }
 
   MaybeNotifyOfValueChange(aContainer);
   FireDelayedEvent(aReorderEvent);
 }
 
 void
-DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
-{
-  for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
-    Accessible* child = aContainer->ContentChildAt(idx);
-    child->SetSurvivingInUpdate(true);
-   }
-
-  AutoTreeMutation mut(aContainer);
-  aContainer->InvalidateChildren();
-  aContainer->EnsureChildren();
-
-  RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
-
-  uint32_t updateFlags = eNoAccessible;
-  for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
-    Accessible* child = aContainer->ContentChildAt(idx);
-    if (child->IsSurvivingInUpdate()) {
-      child->SetSurvivingInUpdate(false);
-      continue;
-    }
-
-    // A new child has been created, update its tree.
-#ifdef A11Y_LOG
-    if (logging::IsEnabled(logging::eTree)) {
-      logging::MsgBegin("TREE", "process content insertion");
-      logging::Node("container", aContainer->GetNode());
-      logging::Node("child", child->GetContent());
-      logging::Address("child", child);
-      logging::MsgEnd();
-    }
-#endif
-
-    updateFlags |= UpdateTreeInternal(child, true, reorderEvent);
-  }
-
-  // Content insertion/removal is not cause of accessible tree change.
-  if (updateFlags == eNoAccessible)
-    return;
-
-  // Check to see if change occurred inside an alert, and fire an EVENT_ALERT
-  // if it did.
-  if (!(updateFlags & eAlertAccessible) &&
-      (aContainer->IsAlert() || aContainer->IsInsideAlert())) {
-    Accessible* ancestor = aContainer;
-    do {
-      if (ancestor->IsAlert()) {
-        FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
-        break;
-      }
-    }
-    while ((ancestor = ancestor->Parent()));
-  }
-
-  MaybeNotifyOfValueChange(aContainer);
-  FireDelayedEvent(reorderEvent);
-}
-
-void
 DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode)
 {
   // If child node is not accessible then look for its accessible children.
   Accessible* child = GetAccessible(aChildNode);
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eTree)) {
     logging::MsgBegin("TREE", "process content removal");
     logging::Node("container", aContainer->GetNode());
@@ -2116,21 +2058,30 @@ DocAccessible::ValidateARIAOwned()
 void
 DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
 {
   nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
 
   MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
   MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
 
+#ifdef A11Y_LOG
+  logging::TreeInfo("aria owns relocation", logging::eVerbose, aOwner);
+#endif
+
   IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
   Accessible* child = nullptr;
 
   uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
   while ((child = iter.Next())) {
+#ifdef A11Y_LOG
+  logging::TreeInfo("aria owns traversal", logging::eVerbose,
+                    "candidate", child, nullptr);
+#endif
+
     // Same child on same position, no change.
     if (child->Parent() == aOwner &&
         child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
       NS_ASSERTION(child == children->ElementAt(arrayIdx), "Not in sync!");
       insertIdx++; arrayIdx++;
       continue;
     }
 
@@ -2148,138 +2099,31 @@ DocAccessible::DoARIAOwnsRelocation(Acce
         parent = parent->Parent();
       }
       // A referred child cannot be a parent of the owner.
       if (parent == child) {
         continue;
       }
     }
 
-    if (child->Parent() == aOwner) {
-      if (child->IsRelocated()) {
-        children->RemoveElement(child);
-      }
-      MoveChild(child, insertIdx);
+    if (MoveChild(child, aOwner, insertIdx)) {
+      child->SetRelocated(true);
       children->InsertElementAt(arrayIdx, child);
       arrayIdx++;
       insertIdx = child->IndexInParent() + 1;
-
-    } else if (SeizeChild(aOwner, child, insertIdx)) {
-      children->InsertElementAt(arrayIdx, child);
-      insertIdx++; arrayIdx++;
     }
   }
 
   // Put back children that are not seized anymore.
   PutChildrenBack(children, arrayIdx);
   if (children->Length() == 0) {
     mARIAOwnsHash.Remove(aOwner);
   }
 }
 
-bool
-DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
-                          int32_t aIdxInParent)
-{
-  Accessible* oldParent = aChild->Parent();
-  if (!oldParent) {
-    NS_ERROR("No parent? The tree is broken!");
-    return false;
-  }
-
-  int32_t oldIdxInParent = aChild->IndexInParent();
-
-#ifdef A11Y_LOG
-  logging::TreeInfo("aria owns seize child", 0,
-                    "old parent", oldParent, "new parent", aNewParent,
-                    "child", aChild, nullptr);
-#endif
-
-  RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
-  RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
-  reorderEvent->AddSubMutationEvent(hideEvent);
-
-  {
-    AutoTreeMutation mut(oldParent);
-    oldParent->RemoveChild(aChild);
-  }
-
-  bool isReinserted = false;
-  {
-    AutoTreeMutation mut(aNewParent);
-    isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
-  }
-
-#ifdef A11Y_LOG
-    logging::TreeInfo("aria owns seize child: new parent tree after",
-                      logging::eVerbose, aNewParent);
-#endif
-
-  if (!isReinserted) {
-    AutoTreeMutation mut(oldParent);
-    oldParent->InsertChildAt(oldIdxInParent, aChild);
-    return false;
-  }
-
-  // The child may be stolen from other ARIA owns element.
-  if (aChild->IsRelocated()) {
-    nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(oldParent);
-    children->RemoveElement(aChild);
-  }
-
-  FireDelayedEvent(hideEvent);
-  MaybeNotifyOfValueChange(oldParent);
-  FireDelayedEvent(reorderEvent);
-
-  reorderEvent = new AccReorderEvent(aNewParent);
-  RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
-  reorderEvent->AddSubMutationEvent(showEvent);
-
-  FireDelayedEvent(showEvent);
-  MaybeNotifyOfValueChange(aNewParent);
-  FireDelayedEvent(reorderEvent);
-
-  aChild->SetRelocated(true);
-  return true;
-}
-
-void
-DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
-{
-  NS_PRECONDITION(aChild->Parent(), "No parent?");
-
-  Accessible* parent = aChild->Parent();
-  RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
-  RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
-  reorderEvent->AddSubMutationEvent(hideEvent);
-
-#ifdef A11Y_LOG
-  logging::TreeInfo("aria owns move child", 0,
-                    "parent", parent, "child", aChild, nullptr);
-#endif
-
-  AutoTreeMutation mut(parent);
-  parent->MoveChild(aIdxInParent, aChild);
-  aChild->SetRelocated(true);
-
-#ifdef A11Y_LOG
-  logging::TreeInfo("aria owns move child: parent tree after",
-                    logging::eVerbose, parent);
-#endif
-
-  FireDelayedEvent(hideEvent);
-
-  RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
-  reorderEvent->AddSubMutationEvent(showEvent);
-  FireDelayedEvent(showEvent);
-
-  MaybeNotifyOfValueChange(parent);
-  FireDelayedEvent(reorderEvent);
-}
-
 void
 DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
                                uint32_t aStartIdx)
 {
   nsTArray<RefPtr<Accessible> > containers;
   for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
     Accessible* child = aChildren->ElementAt(idx);
     if (!child->IsInDocument()) {
@@ -2293,64 +2137,126 @@ DocAccessible::PutChildrenBack(nsTArray<
       continue;
     }
 
 #ifdef A11Y_LOG
     logging::TreeInfo("aria owns put child back", 0,
                       "old parent", owner, "child", child, nullptr);
 #endif
 
-    RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
-    RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
-    reorderEvent->AddSubMutationEvent(hideEvent);
-    FireDelayedEvent(hideEvent);
+    // Unset relocated flag to find an insertion point for the child.
+    child->SetRelocated(false);
 
-    {
-      AutoTreeMutation mut(owner);
-      owner->RemoveChild(child);
-      child->SetRelocated(false);
-    }
-
-    MaybeNotifyOfValueChange(owner);
-    FireDelayedEvent(reorderEvent);
-
-#ifdef A11Y_LOG
-    logging::TreeInfo("aria owns put child back: old parent tree after",
-                      logging::eVerbose, owner);
-#endif
-
-    // and put it back where it belongs to.
+    int32_t idxInParent = -1;
     Accessible* origContainer = GetContainerAccessible(child->GetContent());
     if (origContainer) {
       TreeWalker walker(origContainer);
       if (walker.Seek(child->GetContent())) {
         Accessible* prevChild = walker.Prev();
-        {
-          AutoTreeMutation mut(origContainer);
-          origContainer->InsertAfter(child, prevChild);
-        }
-
-        RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(origContainer);
-        RefPtr<AccMutationEvent> showEvent = new AccShowEvent(child);
-        reorderEvent->AddSubMutationEvent(showEvent);
-        FireDelayedEvent(showEvent);
-        MaybeNotifyOfValueChange(origContainer);
-        FireDelayedEvent(reorderEvent);
-
-#ifdef A11Y_LOG
-        logging::TreeInfo("aria owns put child back: new parent tree after",
-                          logging::eVerbose, origContainer);
-#endif
+        idxInParent = prevChild ? prevChild->IndexInParent() + 1 : 0;
       }
     }
+    MoveChild(child, origContainer, idxInParent);
   }
 
   aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
 }
 
+bool
+DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
+                         int32_t aIdxInParent)
+{
+  MOZ_ASSERT(aChild, "No child");
+  MOZ_ASSERT(aChild->Parent(), "No parent");
+
+  Accessible* curParent = aChild->Parent();
+
+#ifdef A11Y_LOG
+  logging::TreeInfo("move child", 0,
+                    "old parent", curParent, "new parent", aNewParent,
+                    "child", aChild, nullptr);
+#endif
+
+  // If the child was taken from from an ARIA owns element.
+  if (aChild->IsRelocated()) {
+    nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
+    children->RemoveElement(aChild);
+  }
+
+  if (curParent == aNewParent) {
+    MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
+
+    RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
+    RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
+    reorderEvent->AddSubMutationEvent(hideEvent);
+    FireDelayedEvent(hideEvent);
+
+    AutoTreeMutation mut(curParent);
+    curParent->MoveChild(aIdxInParent, aChild);
+
+    RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
+    reorderEvent->AddSubMutationEvent(showEvent);
+    FireDelayedEvent(showEvent);
+
+    MaybeNotifyOfValueChange(curParent);
+    FireDelayedEvent(reorderEvent);
+
+#ifdef A11Y_LOG
+    logging::TreeInfo("move child: parent tree after",
+                      logging::eVerbose, curParent);
+#endif
+    return true;
+  }
+
+  if (!aNewParent->IsAcceptableChild(aChild->GetContent())) {
+    return false;
+  }
+
+  RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
+  RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
+  reorderEvent->AddSubMutationEvent(hideEvent);
+  FireDelayedEvent(hideEvent);
+
+  {
+    AutoTreeMutation mut(curParent);
+    curParent->RemoveChild(aChild);
+  }
+
+  MaybeNotifyOfValueChange(curParent);
+  FireDelayedEvent(reorderEvent);
+
+  // No insertion point for the child.
+  if (aIdxInParent == -1) {
+    return true;
+  }
+
+  {
+    AutoTreeMutation mut(aNewParent);
+    aNewParent->InsertChildAt(aIdxInParent, aChild);
+  }
+
+  reorderEvent = new AccReorderEvent(aNewParent);
+  RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
+  reorderEvent->AddSubMutationEvent(showEvent);
+  FireDelayedEvent(showEvent);
+
+  MaybeNotifyOfValueChange(aNewParent);
+  FireDelayedEvent(reorderEvent);
+
+#ifdef A11Y_LOG
+  logging::TreeInfo("move child: old parent tree after",
+                    logging::eVerbose, curParent);
+  logging::TreeInfo("move child: new parent tree after",
+                    logging::eVerbose, aNewParent);
+#endif
+
+  return true;
+}
+
+
 void
 DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
                                       Accessible** aFocusedAcc)
 {
   // If the accessible is focused then report a focus event after all related
   // mutation events.
   if (aFocusedAcc && !*aFocusedAcc &&
       FocusMgr()->HasDOMFocus(aRoot->GetContent()))
@@ -2361,18 +2267,19 @@ DocAccessible::CacheChildrenInSubtree(Ac
   // Make sure we create accessible tree defined in DOM only, i.e. if accessible
   // provides specific tree (like XUL trees) then tree creation is handled by
   // this accessible.
   uint32_t count = aRoot->ContentChildCount();
   for (uint32_t idx = 0; idx < count; idx++) {
     Accessible* child = aRoot->ContentChildAt(idx);
     NS_ASSERTION(child, "Illicit tree change while tree is created!");
     // Don't cross document boundaries.
-    if (child && child->IsContent())
+    if (child && child->IsContent()) {
       CacheChildrenInSubtree(child, aFocusedAcc);
+    }
   }
 
   // Fire document load complete on ARIA documents.
   // XXX: we should delay an event if the ARIA document has aria-busy.
   if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
     a11y::role role = aRoot->ARIARole();
     if (role == roles::DIALOG || role == roles::DOCUMENT)
       FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -492,21 +492,16 @@ protected:
    *
    * While children are cached we may encounter the case there's no accessible
    * for referred content by related accessible. Store these related nodes to
    * invalidate their containers later.
    */
   void ProcessInvalidationList();
 
   /**
-   * Update the tree on content insertion.
-   */
-  void UpdateTreeOnInsertion(Accessible* aContainer);
-
-  /**
    * Update the accessible tree for content removal.
    */
   void UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode);
 
   /**
    * Helper for UpdateTreeOn methods. Go down to DOM subtree and updates
    * accessible tree. Return one of these flags.
    */
@@ -530,32 +525,24 @@ protected:
   void ValidateARIAOwned();
 
   /**
    * Steals or puts back accessible subtrees.
    */
   void DoARIAOwnsRelocation(Accessible* aOwner);
 
   /**
-   * Moves the child from old parent under new one.
-   */
-  bool SeizeChild(Accessible* aNewParent, Accessible* aChild,
-                  int32_t aIdxInParent);
-
-  /**
-   * Move the child under same parent.
-   */
-  void MoveChild(Accessible* aChild, int32_t aIdxInParent);
-
-  /**
    * Moves children back under their original parents.
    */
   void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
                        uint32_t aStartIdx);
 
+  bool MoveChild(Accessible* aChild, Accessible* aNewParent,
+                 int32_t aIdxInParent);
+
   /**
    * Create accessible tree.
    *
    * @param aRoot       [in] a root of subtree to create
    * @param aFocusedAcc [in, optional] a focused accessible under created
    *                      subtree if any
    */
   void CacheChildrenInSubtree(Accessible* aRoot,
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1884,21 +1884,20 @@ HyperTextAccessible::NativeName(nsString
   if (IsAbbreviation() &&
       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
     aName.CompressWhitespace();
 
   return hasImgAlt ? eNoNameOnPurpose : eNameOK;
 }
 
 void
-HyperTextAccessible::InvalidateChildren()
+HyperTextAccessible::Shutdown()
 {
   mOffsets.Clear();
-
-  AccessibleWrap::InvalidateChildren();
+  AccessibleWrap::Shutdown();
 }
 
 bool
 HyperTextAccessible::RemoveChild(Accessible* aAccessible)
 {
   int32_t childIndex = aAccessible->IndexInParent();
   int32_t count = mOffsets.Length() - childIndex;
   if (count > 0)
--- a/accessible/generic/HyperTextAccessible.h
+++ b/accessible/generic/HyperTextAccessible.h
@@ -55,17 +55,17 @@ public:
 
   // Accessible
   virtual nsIAtom* LandmarkRole() const override;
   virtual int32_t GetLevelInternal() override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
 
-  virtual void InvalidateChildren() override;
+  virtual void Shutdown() override;
   virtual bool RemoveChild(Accessible* aAccessible) override;
   virtual Relation RelationByType(RelationType aType) override;
 
   // HyperTextAccessible (static helper method)
 
   // Convert content offset to rendered text offset
   nsresult ContentToRenderedOffset(nsIFrame *aFrame, int32_t aContentOffset,
                                    uint32_t *aRenderedOffset) const;
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -24,16 +24,25 @@ using namespace mozilla::a11y;
 // OuterDocAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 OuterDocAccessible::
   OuterDocAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   mType = eOuterDocType;
+
+  // Request document accessible for the content document to make sure it's
+  // created. It will appended to outerdoc accessible children asynchronously.
+  nsIDocument* outerDoc = mContent->GetCurrentDoc();
+  if (outerDoc) {
+    nsIDocument* innerDoc = outerDoc->GetSubDocumentFor(mContent);
+    if (innerDoc)
+      GetAccService()->GetDocAccessible(innerDoc);
+  }
 }
 
 OuterDocAccessible::~OuterDocAccessible()
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
@@ -96,31 +105,16 @@ OuterDocAccessible::Shutdown()
 #endif
     RemoveChild(child);
     mDoc->BindChildDocument(child->AsDoc());
   }
 
   AccessibleWrap::Shutdown();
 }
 
-void
-OuterDocAccessible::InvalidateChildren()
-{
-  // Do not invalidate children because DocManager is responsible for
-  // document accessible lifetime when DOM document is created or destroyed. If
-  // DOM document isn't destroyed but its presshell is destroyed (for example,
-  // when DOM node of outerdoc accessible is hidden), then outerdoc accessible
-  // notifies DocManager about this. If presshell is created for existing
-  // DOM document (for example when DOM node of outerdoc accessible is shown)
-  // then allow DocManager to handle this case since the document
-  // accessible is created and appended as a child when it's requested.
-
-  SetChildrenFlag(eChildrenUninitialized);
-}
-
 bool
 OuterDocAccessible::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
 {
   NS_ASSERTION(aAccessible->IsDoc(),
                "OuterDocAccessible should only have document child!");
   // We keep showing the old document for a bit after creating the new one,
   // and while building the new DOM and frame tree. That's done on purpose
   // to avoid weird flashes of default background color.
@@ -163,33 +157,16 @@ OuterDocAccessible::RemoveChild(Accessib
   bool wasRemoved = AccessibleWrap::RemoveChild(child);
 
   NS_ASSERTION(!mChildren.Length(),
                "This child document of outerdoc accessible wasn't removed!");
 
   return wasRemoved;
 }
 
-
-////////////////////////////////////////////////////////////////////////////////
-// Accessible protected
-
-void
-OuterDocAccessible::CacheChildren()
-{
-  // Request document accessible for the content document to make sure it's
-  // created. It will appended to outerdoc accessible children asynchronously.
-  nsIDocument* outerDoc = mContent->GetCurrentDoc();
-  if (outerDoc) {
-    nsIDocument* innerDoc = outerDoc->GetSubDocumentFor(mContent);
-    if (innerDoc)
-      GetAccService()->GetDocAccessible(innerDoc);
-  }
-}
-
 ProxyAccessible*
 OuterDocAccessible::RemoteChildDoc() const
 {
   dom::TabParent* tab = dom::TabParent::GetFrom(GetContent());
   if (!tab)
     return nullptr;
 
   return tab->GetTopLevelDocAccessible();
--- a/accessible/generic/OuterDocAccessible.h
+++ b/accessible/generic/OuterDocAccessible.h
@@ -31,25 +31,21 @@ public:
   ProxyAccessible* RemoteChildDoc() const;
 
   // Accessible
   virtual void Shutdown() override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild) override;
 
-  virtual void InvalidateChildren() override;
   virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
   virtual bool RemoveChild(Accessible* aAccessible) override;
 
 protected:
   virtual ~OuterDocAccessible() override;
-
-  // Accessible
-  virtual void CacheChildren() override;
 };
 
 inline OuterDocAccessible*
 Accessible::AsOuterDoc()
 {
   return IsOuterDoc() ? static_cast<OuterDocAccessible*>(this) : nullptr;
 }
 
--- a/accessible/generic/TextLeafAccessible.cpp
+++ b/accessible/generic/TextLeafAccessible.cpp
@@ -15,16 +15,17 @@ using namespace mozilla::a11y;
 // TextLeafAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 TextLeafAccessible::
   TextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   LinkableAccessible(aContent, aDoc)
 {
   mType = eTextLeafType;
+  mStateFlags |= eNoKidsFromDOM;
 }
 
 TextLeafAccessible::~TextLeafAccessible()
 {
 }
 
 role
 TextLeafAccessible::NativeRole()
@@ -45,14 +46,8 @@ TextLeafAccessible::AppendTextTo(nsAStri
 
 ENameValueFlag
 TextLeafAccessible::Name(nsString& aName)
 {
   // Text node, ARIA can't be used.
   aName = mText;
   return eNameOK;
 }
-
-void
-TextLeafAccessible::CacheChildren()
-{
-  // No children for text accessible.
-}
--- a/accessible/generic/TextLeafAccessible.h
+++ b/accessible/generic/TextLeafAccessible.h
@@ -26,20 +26,16 @@ public:
                             uint32_t aLength = UINT32_MAX) override;
   virtual ENameValueFlag Name(nsString& aName) override;
 
   // TextLeafAccessible
   void SetText(const nsAString& aText) { mText = aText; }
   const nsString& Text() const { return mText; }
 
 protected:
-  // Accessible
-  virtual void CacheChildren() override;
-
-protected:
   nsString mText;
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcast method
 
 inline TextLeafAccessible*
--- a/accessible/html/HTMLImageMapAccessible.cpp
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -25,16 +25,18 @@ using namespace mozilla::a11y;
 // HTMLImageMapAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLImageMapAccessible::
   HTMLImageMapAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   ImageAccessibleWrap(aContent, aDoc)
 {
   mType = eImageMapType;
+
+  UpdateChildAreas(false);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLImageMapAccessible: nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLImageMapAccessible, ImageAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -146,26 +148,16 @@ HTMLImageMapAccessible::GetChildAccessib
     if (area->GetContent() == aNode)
       return area;
   }
 
   return nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// HTMLImageMapAccessible: Accessible protected
-
-void
-HTMLImageMapAccessible::CacheChildren()
-{
-  UpdateChildAreas(false);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // HTMLAreaAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLAreaAccessible::
   HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   HTMLLinkAccessible(aContent, aDoc)
 {
   // Make HTML area DOM element not accessible. HTML image map accessible
@@ -226,25 +218,16 @@ HTMLAreaAccessible::StartOffset()
 }
 
 uint32_t
 HTMLAreaAccessible::EndOffset()
 {
   return IndexInParent() + 1;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// HTMLAreaAccessible: Accessible protected
-
-void
-HTMLAreaAccessible::CacheChildren()
-{
-  // No children for aria accessible.
-}
-
 nsRect
 HTMLAreaAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const
 {
   nsIFrame* frame = GetFrame();
   if (!frame)
     return nsRect();
 
   nsImageFrame* imageFrame = do_QueryFrame(frame);
--- a/accessible/html/HTMLImageMapAccessible.h
+++ b/accessible/html/HTMLImageMapAccessible.h
@@ -39,19 +39,16 @@ public:
 
   /**
    * Return accessible of child node.
    */
   Accessible* GetChildAccessibleFor(const nsINode* aNode) const;
 
 protected:
   virtual ~HTMLImageMapAccessible() { }
-
-  // Accessible
-  virtual void CacheChildren() override;
 };
 
 /**
  * Accessible for image map areas - must be child of image.
  */
 class HTMLAreaAccessible final : public HTMLLinkAccessible
 {
 public:
@@ -63,20 +60,22 @@ public:
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild) override;
   virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
 
   // HyperLinkAccessible
   virtual uint32_t StartOffset() override;
   virtual uint32_t EndOffset() override;
 
+  virtual bool IsAcceptableChild(nsIContent* aEl) const override
+    { return false; }
+
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) override;
-  virtual void CacheChildren() override;
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
 inline HTMLImageMapAccessible*
 Accessible::AsImageMap()
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -332,77 +332,57 @@ HTMLSelectOptGroupAccessible::DoAction(u
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLComboboxAccessible::
   HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   mType = eHTMLComboboxType;
   mGenericTypes |= eCombobox;
+  mStateFlags |= eNoKidsFromDOM;
+
+  nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
+  if (comboFrame) {
+    nsIFrame* listFrame = comboFrame->GetDropDown();
+    if (listFrame) {
+      mListAccessible = new HTMLComboboxListAccessible(mParent, mContent, mDoc);
+      Document()->BindToDocument(mListAccessible, nullptr);
+      AppendChild(mListAccessible);
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible: Accessible
 
 role
 HTMLComboboxAccessible::NativeRole()
 {
   return roles::COMBOBOX;
 }
 
-void
-HTMLComboboxAccessible::InvalidateChildren()
-{
-  AccessibleWrap::InvalidateChildren();
-
-  if (mListAccessible)
-    mListAccessible->InvalidateChildren();
-}
-
 bool
 HTMLComboboxAccessible::RemoveChild(Accessible* aChild)
 {
   MOZ_ASSERT(aChild == mListAccessible);
   if (AccessibleWrap::RemoveChild(aChild)) {
     mListAccessible = nullptr;
     return true;
   }
   return false;
 }
 
 void
-HTMLComboboxAccessible::CacheChildren()
-{
-  nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
-  if (!comboFrame)
-    return;
-
-  nsIFrame* listFrame = comboFrame->GetDropDown();
-  if (!listFrame)
-    return;
-
-  if (!mListAccessible) {
-    mListAccessible = new HTMLComboboxListAccessible(mParent, mContent, mDoc);
-
-    // Initialize and put into cache.
-    Document()->BindToDocument(mListAccessible, nullptr);
-  }
-
-  if (AppendChild(mListAccessible)) {
-    // Cache combobox option accessibles so that we build complete accessible
-    // tree for combobox.
-    mListAccessible->EnsureChildren();
-  }
-}
-
-void
 HTMLComboboxAccessible::Shutdown()
 {
   MOZ_ASSERT(mDoc->IsDefunct() || !mListAccessible);
-  mListAccessible = nullptr;
+  if (mListAccessible) {
+    mListAccessible->Shutdown();
+    mListAccessible = nullptr;
+  }
 
   AccessibleWrap::Shutdown();
 }
 
 uint64_t
 HTMLComboboxAccessible::NativeState()
 {
   // As a HTMLComboboxAccessible we can have the following states:
--- a/accessible/html/HTMLSelectAccessible.h
+++ b/accessible/html/HTMLSelectAccessible.h
@@ -163,35 +163,31 @@ public:
   virtual ~HTMLComboboxAccessible() {}
 
   // Accessible
   virtual void Shutdown() override;
   virtual void Description(nsString& aDescription) override;
   virtual void Value(nsString& aValue) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
-  virtual void InvalidateChildren() override;
   virtual bool RemoveChild(Accessible* aChild) override;
 
   // ActionAccessible
   virtual uint8_t ActionCount() override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) override;
 
   // Widgets
   virtual bool IsWidget() const override;
   virtual bool IsActiveWidget() const override;
   virtual bool AreItemsOperable() const override;
   virtual Accessible* CurrentItem() override;
   virtual void SetCurrentItem(Accessible* aItem) override;
 
 protected:
-  // Accessible
-  virtual void CacheChildren() override;
-
   /**
    * Return selected option.
    */
   Accessible* SelectedOption() const;
 
 private:
   RefPtr<HTMLComboboxListAccessible> mListAccessible;
 };
--- a/accessible/mac/AccessibleWrap.h
+++ b/accessible/mac/AccessibleWrap.h
@@ -41,17 +41,16 @@ public: // construction, destruction
   /**
    * The objective-c |Class| type that this accessible's native object
    * should be instantied with.   used on runtime to determine the
    * right type for this accessible's associated native object.
    */
   virtual Class GetNativeType ();
 
   virtual void Shutdown () override;
-  virtual void InvalidateChildren() override;
 
   virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
   virtual bool RemoveChild(Accessible* aAccessible) override;
 
   virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
 
   /**
    * Ignored means that the accessible might still have children, but is not
--- a/accessible/mac/AccessibleWrap.mm
+++ b/accessible/mac/AccessibleWrap.mm
@@ -130,28 +130,16 @@ AccessibleWrap::HandleAccEvent(AccEvent*
 
   FireNativeEvent(nativeAcc, eventType);
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
-void
-AccessibleWrap::InvalidateChildren()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  [GetNativeObject() invalidateChildren];
-
-  Accessible::InvalidateChildren();
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
 bool
 AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
 {
   bool inserted = Accessible::InsertChildAt(aIdx, aAccessible);
   if (inserted && mNativeObject)
     [mNativeObject appendChild:aAccessible];
 
   return inserted;
--- a/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp
+++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp
@@ -14,19 +14,23 @@ using namespace mozilla::a11y;
 // HTMLWin32ObjectOwnerAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLWin32ObjectOwnerAccessible::
   HTMLWin32ObjectOwnerAccessible(nsIContent* aContent,
                                  DocAccessible* aDoc, void* aHwnd) :
   AccessibleWrap(aContent, aDoc), mHwnd(aHwnd)
 {
+  mStateFlags |= eNoKidsFromDOM;
+
   // Our only child is a HTMLWin32ObjectAccessible object.
-  if (mHwnd)
+  if (mHwnd) {
     mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd, aDoc);
+    AppendChild(mNativeAccessible);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLWin32ObjectOwnerAccessible: Accessible implementation
 
 void
 HTMLWin32ObjectOwnerAccessible::Shutdown()
 {
@@ -44,27 +48,16 @@ bool
 HTMLWin32ObjectOwnerAccessible::NativelyUnavailable() const
 {
   // XXX: No HWND means this is windowless plugin which is not accessible in
   // the meantime.
   return !mHwnd;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// HTMLWin32ObjectOwnerAccessible: Accessible protected implementation
-
-void
-HTMLWin32ObjectOwnerAccessible::CacheChildren()
-{
-  if (mNativeAccessible)
-    AppendChild(mNativeAccessible);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // HTMLWin32ObjectAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLWin32ObjectAccessible::HTMLWin32ObjectAccessible(void* aHwnd,
                                                      DocAccessible* aDoc) :
   DummyAccessible(aDoc)
 {
   mHwnd = aHwnd;
--- a/accessible/windows/msaa/HTMLWin32ObjectAccessible.h
+++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.h
@@ -27,20 +27,16 @@ public:
   virtual ~HTMLWin32ObjectOwnerAccessible() {}
 
   // Accessible
   virtual void Shutdown();
   virtual mozilla::a11y::role NativeRole();
   virtual bool NativelyUnavailable() const;
 
 protected:
-
-  // Accessible
-  virtual void CacheChildren();
-
   void* mHwnd;
   RefPtr<Accessible> mNativeAccessible;
 };
 
 /**
   * This class is used only internally, we never! send out an IAccessible linked
   *   back to this object. This class is used to represent a plugin object when
   *   referenced as a child or sibling of another Accessible node. We need only
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -1065,16 +1065,17 @@ XULTreeItemAccessibleBase::GetCellName(n
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeItemAccessible::
   XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc,
                         Accessible* aParent, nsITreeBoxObject* aTree,
                         nsITreeView* aTreeView, int32_t aRow) :
   XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow)
 {
+  mStateFlags |= eNoKidsFromDOM;
   mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree);
   GetCellName(mColumn, mCachedName);
 }
 
 XULTreeItemAccessible::~XULTreeItemAccessible()
 {
 }
 
@@ -1138,24 +1139,16 @@ XULTreeItemAccessible::RowInvalidated(in
   Name(name);
 
   if (name != mCachedName) {
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
     mCachedName = name;
   }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// XULTreeItemAccessible: Accessible protected implementation
-
-void
-XULTreeItemAccessible::CacheChildren()
-{
-}
-
 
 ////////////////////////////////////////////////////////////////////////////////
 //  XULTreeColumAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeColumAccessible::
   XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULColumAccessible(aContent, aDoc)
--- a/accessible/xul/XULTreeAccessible.h
+++ b/accessible/xul/XULTreeAccessible.h
@@ -236,19 +236,16 @@ public:
   virtual a11y::role NativeRole() override;
 
   // XULTreeItemAccessibleBase
   virtual void RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) override;
 
 protected:
   virtual ~XULTreeItemAccessible();
 
-  // Accessible
-  virtual void CacheChildren() override;
-
   // XULTreeItemAccessible
   nsCOMPtr<nsITreeColumn> mColumn;
   nsString mCachedName;
 };
 
 
 /**
  * Accessible class for columns element of XUL tree.
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -244,16 +244,17 @@ XULTreeGridAccessible::CreateTreeItemAcc
 XULTreeGridRowAccessible::
   XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc,
                            Accessible* aTreeAcc, nsITreeBoxObject* aTree,
                            nsITreeView* aTreeView, int32_t aRow) :
   XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView, aRow),
   mAccessibleCache(kDefaultTreeCacheLength)
 {
   mGenericTypes |= eTableRow;
+  mStateFlags |= eNoKidsFromDOM;
 }
 
 XULTreeGridRowAccessible::~XULTreeGridRowAccessible()
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridRowAccessible: nsISupports and cycle collection implementation
@@ -404,23 +405,16 @@ XULTreeGridRowAccessible::RowInvalidated
     }
   }
 
   if (nameChanged)
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
 
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// XULTreeGridRowAccessible: Accessible protected implementation
-
-void
-XULTreeGridRowAccessible::CacheChildren()
-{
-}
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeGridCellAccessible::
   XULTreeGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc,
                             XULTreeGridRowAccessible* aRowAcc,
--- a/accessible/xul/XULTreeGridAccessible.h
+++ b/accessible/xul/XULTreeGridAccessible.h
@@ -92,19 +92,16 @@ public:
   // XULTreeItemAccessibleBase
   virtual XULTreeGridCellAccessible* GetCellAccessible(nsITreeColumn* aColumn)
     const override final;
   virtual void RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) override;
 
 protected:
   virtual ~XULTreeGridRowAccessible();
 
-  // Accessible
-  virtual void CacheChildren() override;
-
   // XULTreeItemAccessibleBase
   mutable nsRefPtrHashtable<nsPtrHashKey<const void>, XULTreeGridCellAccessible>
     mAccessibleCache;
 };
 
 
 /**
  * Represents an accessible for XUL tree cell in the case when XUL tree has
--- a/b2g/moz.configure
+++ b/b2g/moz.configure
@@ -6,9 +6,19 @@
 
 option('--with-gonk', nargs=1, help='Path to the gonk base directory')
 
 @depends('--with-gonk')
 def gonkdir(value):
     return value[0] if value else ''
 
 
+option('--with-gonk-toolchain-prefix', nargs=1,
+       help='Prefix to gonk toolchain commands')
+
+@depends_if('--with-gonk-toolchain-prefix')
+def gonk_toolchain_prefix(value):
+    return value
+
+imply_option('--with-toolchain-prefix', gonk_toolchain_prefix)
+
+
 include('common.configure')
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1438,21 +1438,16 @@ pref("security.mixed_content.block_activ
 pref("security.insecure_password.ui.enabled", true);
 #else
 pref("security.insecure_password.ui.enabled", false);
 #endif
 
 // 1 = allow MITM for certificate pinning checks.
 pref("security.cert_pinning.enforcement_level", 1);
 
-// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
-// See the comment in CertVerifier.cpp.
-// 0 = allow SHA-1
-pref("security.pki.sha1_enforcement_level", 0);
-
 // Required blocklist freshness for OneCRL OCSP bypass
 // (default is 1.25x extensions.blocklist.interval, or 30 hours)
 pref("security.onecrl.maximum_staleness_in_seconds", 108000);
 
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
 // If this turns true, Moz*Gesture events are not called stopPropagation()
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1,8 +1,9 @@
+/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 sw=2 sts=2 et 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/.
 
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
 Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
@@ -772,17 +773,18 @@ nsContextMenu.prototype = {
             this.bgImageURL = makeURLAbsolute(bodyElt.baseURI,
                                               computedURL);
           }
         }
       }
       else if ((this.target instanceof HTMLEmbedElement ||
                 this.target instanceof HTMLObjectElement ||
                 this.target instanceof HTMLAppletElement) &&
-               this.target.matches(":-moz-handler-clicktoplay")) {
+               this.target.displayedType == HTMLObjectElement.TYPE_NULL &&
+               this.target.pluginFallbackType == HTMLObjectElement.PLUGIN_CLICK_TO_PLAY) {
         this.onCTPPlugin = true;
       }
 
       this.canSpellCheck = this._isSpellCheckEnabled(this.target);
     }
     else if (this.target.nodeType == Node.TEXT_NODE) {
       // For text nodes, look at the parent node to determine the spellcheck attribute.
       this.canSpellCheck = this.target.parentNode &&
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -128,58 +128,28 @@ case "$target" in
     android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_name"
 
     if test -d "$android_platform" ; then
         AC_MSG_RESULT([$android_platform])
     else
         AC_MSG_ERROR([not found. Please check your NDK. With the current configuration, it should be in $android_platform])
     fi
 
-    dnl set up compilers
     TOOLCHAIN_PREFIX="$android_toolchain/bin/$android_tool_prefix-"
-    AS="$android_toolchain"/bin/"$android_tool_prefix"-as
-    if test -z "$CC"; then
-        CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc
-    fi
-    if test -z "$CXX"; then
-        CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++
-    fi
-    if test -z "$CPP"; then
-        CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp
-    fi
-    LD="$android_toolchain"/bin/"$android_tool_prefix"-ld
-    AR="$android_toolchain"/bin/"$android_tool_prefix"-ar
-    RANLIB="$android_toolchain"/bin/"$android_tool_prefix"-ranlib
-    STRIP="$android_toolchain"/bin/"$android_tool_prefix"-strip
-    OBJCOPY="$android_toolchain"/bin/"$android_tool_prefix"-objcopy
 
     CPPFLAGS="-idirafter $android_platform/usr/include $CPPFLAGS"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions $CXXFLAGS"
     ASFLAGS="-idirafter $android_platform/usr/include -DANDROID $ASFLAGS"
 
     dnl Add -llog by default, since we use it all over the place.
     dnl Add --allow-shlib-undefined, because libGLESv2 links to an
     dnl undefined symbol (present on the hardware, just not in the
     dnl NDK.)
     LDFLAGS="-mandroid -L$android_platform/usr/lib -Wl,-rpath-link=$android_platform/usr/lib --sysroot=$android_platform -llog -Wl,--allow-shlib-undefined $LDFLAGS"
-    dnl prevent cross compile section from using these flags as host flags
-    if test -z "$HOST_CPPFLAGS" ; then
-        HOST_CPPFLAGS=" "
-    fi
-    if test -z "$HOST_CFLAGS" ; then
-        HOST_CFLAGS=" "
-    fi
-    if test -z "$HOST_CXXFLAGS" ; then
-        HOST_CXXFLAGS=" "
-    fi
-    if test -z "$HOST_LDFLAGS" ; then
-        HOST_LDFLAGS=" "
-    fi
-
     ANDROID_NDK="${android_ndk}"
     ANDROID_TOOLCHAIN="${android_toolchain}"
     ANDROID_PLATFORM="${android_platform}"
 
     AC_DEFINE(ANDROID)
     AC_SUBST(ANDROID_NDK)
     AC_SUBST(ANDROID_TOOLCHAIN)
     AC_SUBST(ANDROID_PLATFORM)
--- a/build/autoconf/config.guess
+++ b/build/autoconf/config.guess
@@ -1,13 +1,13 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2014 Free Software Foundation, Inc.
+#   Copyright 1992-2016 Free Software Foundation, Inc.
 
-timestamp='2014-02-12'
+timestamp='2016-03-24'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,22 +19,22 @@ timestamp='2014-02-12'
 #
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # configuration script generated by Autoconf, you may include it under
 # the same distribution terms that you use for the rest of that
 # program.  This Exception is an additional permission under section 7
 # of the GNU General Public License, version 3 ("GPLv3").
 #
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
 #
 # You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
 #
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
 
 
 me=`echo "$0" | sed -e 's,.*/,,'`
 
 usage="\
 Usage: $0 [OPTION]
 
 Output the configuration name of the system \`$me' is run on.
@@ -45,17 +45,17 @@ Operation modes:
   -v, --version      print version number, then exit
 
 Report bugs and patches to <config-patches@gnu.org>."
 
 version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2014 Free Software Foundation, Inc.
+Copyright 1992-2016 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 
 help="
 Try \`$me --help' for more information."
 
 # Parse command line
@@ -163,135 +163,156 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:$
 	# switched to ELF, *-*-netbsd* would select the old
 	# object file format.  This provides both forward
 	# compatibility and a consistent mechanism for selecting the
 	# object file format.
 	#
 	# Note: NetBSD doesn't particularly care about the vendor
 	# portion of the name.  We always set it to "unknown".
 	sysctl="sysctl -n hw.machine_arch"
-	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || \
+	    echo unknown)`
 	case "${UNAME_MACHINE_ARCH}" in
 	    armeb) machine=armeb-unknown ;;
 	    arm*) machine=arm-unknown ;;
 	    sh3el) machine=shl-unknown ;;
 	    sh3eb) machine=sh-unknown ;;
 	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
 	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
 	esac
 	# The Operating System including object format, if it has switched
 	# to ELF recently, or will in the future.
 	case "${UNAME_MACHINE_ARCH}" in
-	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+	    arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
 		eval $set_cc_for_build
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
 			| grep -q __ELF__
 		then
 		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
 		    # Return netbsd for either.  FIX?
 		    os=netbsd
 		else
 		    os=netbsdelf
 		fi
 		;;
 	    *)
 		os=netbsd
 		;;
 	esac
+	# Determine ABI tags.
+	case "${UNAME_MACHINE_ARCH}" in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+		;;
+	esac
 	# The OS release
 	# Debian GNU/NetBSD machines have a different userland, and
 	# thus, need a distinct triplet. However, they do not need
 	# kernel version information, so it can be replaced with a
 	# suitable tag, in the style of linux-gnu.
 	case "${UNAME_VERSION}" in
 	    Debian*)
 		release='-gnu'
 		;;
 	    *)
-		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
 		;;
 	esac
 	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
 	# contains redundant information, the shorter form:
 	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-	echo "${machine}-${os}${release}"
+	echo "${machine}-${os}${release}${abi}"
 	exit ;;
     *:Bitrig:*:*)
 	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
 	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
 	exit ;;
     *:OpenBSD:*:*)
 	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
 	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
 	exit ;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}
+	exit ;;
     *:ekkoBSD:*:*)
 	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
 	exit ;;
     *:SolidBSD:*:*)
 	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
 	exit ;;
     macppc:MirBSD:*:*)
 	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
 	exit ;;
     *:MirBSD:*:*)
 	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
 	exit ;;
+    *:Sortix:*:*)
+	echo ${UNAME_MACHINE}-unknown-sortix
+	exit ;;
     alpha:OSF1:*:*)
 	case $UNAME_RELEASE in
 	*4.0)
 		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
 		;;
 	*5.*)
 		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
 		;;
 	esac
 	# According to Compaq, /usr/sbin/psrinfo has been available on
 	# OSF/1 and Tru64 systems produced since 1995.  I hope that
 	# covers most systems running today.  This code pipes the CPU
 	# types through head -n 1, so we only detect the type of CPU 0.
 	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
 	case "$ALPHA_CPU_TYPE" in
 	    "EV4 (21064)")
-		UNAME_MACHINE="alpha" ;;
+		UNAME_MACHINE=alpha ;;
 	    "EV4.5 (21064)")
-		UNAME_MACHINE="alpha" ;;
+		UNAME_MACHINE=alpha ;;
 	    "LCA4 (21066/21068)")
-		UNAME_MACHINE="alpha" ;;
+		UNAME_MACHINE=alpha ;;
 	    "EV5 (21164)")
-		UNAME_MACHINE="alphaev5" ;;
+		UNAME_MACHINE=alphaev5 ;;
 	    "EV5.6 (21164A)")
-		UNAME_MACHINE="alphaev56" ;;
+		UNAME_MACHINE=alphaev56 ;;
 	    "EV5.6 (21164PC)")
-		UNAME_MACHINE="alphapca56" ;;
+		UNAME_MACHINE=alphapca56 ;;
 	    "EV5.7 (21164PC)")
-		UNAME_MACHINE="alphapca57" ;;
+		UNAME_MACHINE=alphapca57 ;;
 	    "EV6 (21264)")
-		UNAME_MACHINE="alphaev6" ;;
+		UNAME_MACHINE=alphaev6 ;;
 	    "EV6.7 (21264A)")
-		UNAME_MACHINE="alphaev67" ;;
+		UNAME_MACHINE=alphaev67 ;;
 	    "EV6.8CB (21264C)")
-		UNAME_MACHINE="alphaev68" ;;
+		UNAME_MACHINE=alphaev68 ;;
 	    "EV6.8AL (21264B)")
-		UNAME_MACHINE="alphaev68" ;;
+		UNAME_MACHINE=alphaev68 ;;
 	    "EV6.8CX (21264D)")
-		UNAME_MACHINE="alphaev68" ;;
+		UNAME_MACHINE=alphaev68 ;;
 	    "EV6.9A (21264/EV69A)")
-		UNAME_MACHINE="alphaev69" ;;
+		UNAME_MACHINE=alphaev69 ;;
 	    "EV7 (21364)")
-		UNAME_MACHINE="alphaev7" ;;
+		UNAME_MACHINE=alphaev7 ;;
 	    "EV7.9 (21364A)")
-		UNAME_MACHINE="alphaev79" ;;
+		UNAME_MACHINE=alphaev79 ;;
 	esac
 	# A Pn.n version is a patched version.
 	# A Vn.n version is a released version.
 	# A Tn.n version is a released field test version.
 	# A Xn.n version is an unreleased experimental baselevel.
 	# 1.2 uses "1.2" for uname -r.
-	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
 	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
 	exitcode=$?
 	trap '' 0
 	exit $exitcode ;;
     Alpha\ *:Windows_NT*:*)
 	# How do we know it's Interix rather than the generic POSIX subsystem?
 	# Should we change UNAME_MACHINE based on the output of uname instead
 	# of the specific Alpha model?
@@ -354,26 +375,26 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:$
     sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
 	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	exit ;;
     i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
 	echo i386-pc-auroraux${UNAME_RELEASE}
 	exit ;;
     i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
 	eval $set_cc_for_build
-	SUN_ARCH="i386"
+	SUN_ARCH=i386
 	# If there is a compiler, see if it is configured for 64-bit objects.
 	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
 	# This test works for both compilers.
-	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+	if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
 	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
 		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
 		grep IS_64BIT_ARCH >/dev/null
 	    then
-		SUN_ARCH="x86_64"
+		SUN_ARCH=x86_64
 	    fi
 	fi
 	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	exit ;;
     sun4*:SunOS:6*:*)
 	# According to config.sub, this is the proper way to canonicalize
 	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
 	# it's likely to be more like Solaris than SunOS4.
@@ -388,17 +409,17 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:$
 	# Japanese Language versions have a version number like `4.1.3-JL'.
 	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
 	exit ;;
     sun3*:SunOS:*:*)
 	echo m68k-sun-sunos${UNAME_RELEASE}
 	exit ;;
     sun*:*:4.2BSD:*)
 	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
-	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3
 	case "`/bin/arch`" in
 	    sun3)
 		echo m68k-sun-sunos${UNAME_RELEASE}
 		;;
 	    sun4)
 		echo sparc-sun-sunos${UNAME_RELEASE}
 		;;
 	esac
@@ -574,18 +595,19 @@ EOF
 	exit ;;
     *:AIX:*:[4567])
 	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
 	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
 		IBM_ARCH=rs6000
 	else
 		IBM_ARCH=powerpc
 	fi
-	if [ -x /usr/bin/oslevel ] ; then
-		IBM_REV=`/usr/bin/oslevel`
+	if [ -x /usr/bin/lslpp ] ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
 	else
 		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
 	fi
 	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
 	exit ;;
     *:AIX:*:*)
 	echo rs6000-ibm-aix
 	exit ;;
@@ -612,23 +634,23 @@ EOF
 	case "${UNAME_MACHINE}" in
 	    9000/31? )            HP_ARCH=m68000 ;;
 	    9000/[34]?? )         HP_ARCH=m68k ;;
 	    9000/[678][0-9][0-9])
 		if [ -x /usr/bin/getconf ]; then
 		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
 		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
 		    case "${sc_cpu_version}" in
-		      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
-		      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
 		      532)                      # CPU_PA_RISC2_0
 			case "${sc_kernel_bits}" in
-			  32) HP_ARCH="hppa2.0n" ;;
-			  64) HP_ARCH="hppa2.0w" ;;
-			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
 			esac ;;
 		    esac
 		fi
 		if [ "${HP_ARCH}" = "" ]; then
 		    eval $set_cc_for_build
 		    sed 's/^		//' << EOF >$dummy.c
 
 		#define _HPUX_SOURCE
@@ -661,35 +683,35 @@ EOF
 			}
 		    exit (0);
 		}
 EOF
 		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
 		    test -z "$HP_ARCH" && HP_ARCH=hppa
 		fi ;;
 	esac
-	if [ ${HP_ARCH} = "hppa2.0w" ]
+	if [ ${HP_ARCH} = hppa2.0w ]
 	then
 	    eval $set_cc_for_build
 
 	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
 	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
 	    # generating 64-bit code.  GNU and HP use different nomenclature:
 	    #
 	    # $ CC_FOR_BUILD=cc ./config.guess
 	    # => hppa2.0w-hp-hpux11.23
 	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
 	    # => hppa64-hp-hpux11.23
 
 	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
 		grep -q __LP64__
 	    then
-		HP_ARCH="hppa2.0w"
+		HP_ARCH=hppa2.0w
 	    else
-		HP_ARCH="hppa64"
+		HP_ARCH=hppa64
 	    fi
 	fi
 	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
 	exit ;;
     ia64:HP-UX:*:*)
 	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
 	echo ia64-hp-hpux${HPUX_REV}
 	exit ;;
@@ -784,24 +806,24 @@ EOF
 	exit ;;
     CRAY*SV1:*:*:*)
 	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
 	exit ;;
     *:UNICOS/mp:*:*)
 	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
 	exit ;;
     F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
-	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
 	FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
 	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
 	exit ;;
     5000:UNIX_System_V:4.*:*)
-	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
 	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
 	exit ;;
     i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
 	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
 	exit ;;
     sparc*:BSD/OS:*:*)
 	echo sparc-unknown-bsdi${UNAME_RELEASE}
 	exit ;;
@@ -821,17 +843,17 @@ EOF
 	echo ${UNAME_MACHINE}-pc-cygwin
 	exit ;;
     *:MINGW64*:*)
 	echo ${UNAME_MACHINE}-pc-mingw64
 	exit ;;
     *:MINGW*:*)
 	echo ${UNAME_MACHINE}-pc-mingw32
 	exit ;;
-    i*:MSYS*:*)
+    *:MSYS*:*)
 	echo ${UNAME_MACHINE}-pc-msys
 	exit ;;
     i*:windows32*:*)
 	# uname -m includes "-pc" on this system.
 	echo ${UNAME_MACHINE}-mingw32
 	exit ;;
     i*:PW*:*)
 	echo ${UNAME_MACHINE}-pc-pw32
@@ -896,17 +918,17 @@ EOF
 	  EV56)  UNAME_MACHINE=alphaev56 ;;
 	  PCA56) UNAME_MACHINE=alphapca56 ;;
 	  PCA57) UNAME_MACHINE=alphapca56 ;;
 	  EV6)   UNAME_MACHINE=alphaev6 ;;
 	  EV67)  UNAME_MACHINE=alphaev67 ;;
 	  EV68*) UNAME_MACHINE=alphaev68 ;;
 	esac
 	objdump --private-headers /bin/sh | grep -q ld.so.1
-	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     arc:Linux:*:* | arceb:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     arm*:Linux:*:*)
 	eval $set_cc_for_build
 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
@@ -927,28 +949,34 @@ EOF
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     cris:Linux:*:*)
 	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
     crisv32:Linux:*:*)
 	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
+    e2k:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
     frv:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     hexagon:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:Linux:*:*)
 	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
 	exit ;;
     ia64:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
+    k1om:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
     m32r*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     m68*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     mips:Linux:*:* | mips64:Linux:*:*)
 	eval $set_cc_for_build
@@ -964,20 +992,20 @@ EOF
 	#else
 	CPU=
 	#endif
 	#endif
 EOF
 	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
 	;;
-    or1k:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-${LIBC}
 	exit ;;
-    or32:Linux:*:*)
+    or32:Linux:*:* | or1k*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     padre:Linux:*:*)
 	echo sparc-unknown-linux-${LIBC}
 	exit ;;
     parisc64:Linux:*:* | hppa64:Linux:*:*)
 	echo hppa64-unknown-linux-${LIBC}
 	exit ;;
@@ -1015,17 +1043,17 @@ EOF
 	exit ;;
     tile*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     vax:Linux:*:*)
 	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
 	exit ;;
     x86_64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
 	exit ;;
     xtensa*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:DYNIX/ptx:4*:*)
 	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
 	# earlier versions are messed up and put the nodename in both
 	# sysname and nodename.
@@ -1094,17 +1122,17 @@ EOF
 		echo ${UNAME_MACHINE}-pc-sysv32
 	fi
 	exit ;;
     pc:*:*:*)
 	# Left here for compatibility:
 	# uname -m prints for DJGPP always 'pc', but it prints nothing about
 	# the processor, so we play safe by assuming i586.
 	# Note: whatever this is, it MUST be the same as what config.sub
-	# prints for the "djgpp" host, or else GDB configury will decide that
+	# prints for the "djgpp" host, or else GDB configure will decide that
 	# this is a cross-build.
 	echo i586-pc-msdosdjgpp
 	exit ;;
     Intel:Mach:3*:*)
 	echo i386-pc-mach3
 	exit ;;
     paragon:*:*:*)
 	echo i860-intel-osf1
@@ -1243,30 +1271,33 @@ EOF
 	echo sx7-nec-superux${UNAME_RELEASE}
 	exit ;;
     SX-8:SUPER-UX:*:*)
 	echo sx8-nec-superux${UNAME_RELEASE}
 	exit ;;
     SX-8R:SUPER-UX:*:*)
 	echo sx8r-nec-superux${UNAME_RELEASE}
 	exit ;;
+    SX-ACE:SUPER-UX:*:*)
+	echo sxace-nec-superux${UNAME_RELEASE}
+	exit ;;
     Power*:Rhapsody:*:*)
 	echo powerpc-apple-rhapsody${UNAME_RELEASE}
 	exit ;;
     *:Rhapsody:*:*)
 	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
 	exit ;;
     *:Darwin:*:*)
 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
 	eval $set_cc_for_build
 	if test "$UNAME_PROCESSOR" = unknown ; then
 	    UNAME_PROCESSOR=powerpc
 	fi
 	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
-	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+	    if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
 		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
 		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
 		    grep IS_64BIT_ARCH >/dev/null
 		then
 		    case $UNAME_PROCESSOR in
 			i386) UNAME_PROCESSOR=x86_64 ;;
 			powerpc) UNAME_PROCESSOR=powerpc64 ;;
 		    esac
@@ -1280,17 +1311,17 @@ EOF
 	    # processor. This is not true of the ARM version of Darwin
 	    # that Apple uses in portable devices.
 	    UNAME_PROCESSOR=x86_64
 	fi
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
 	exit ;;
     *:procnto*:*:* | *:QNX:[0123456789]*:*)
 	UNAME_PROCESSOR=`uname -p`
-	if test "$UNAME_PROCESSOR" = "x86"; then
+	if test "$UNAME_PROCESSOR" = x86; then
 		UNAME_PROCESSOR=i386
 		UNAME_MACHINE=pc
 	fi
 	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
 	exit ;;
     *:QNX:*:4*)
 	echo i386-pc-qnx
 	exit ;;
@@ -1311,17 +1342,17 @@ EOF
 	exit ;;
     DS/*:UNIX_System_V:*:*)
 	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
 	exit ;;
     *:Plan9:*:*)
 	# "uname -m" is not consistent, so use $cputype instead. 386
 	# is converted to i386 for consistency with other x86
 	# operating systems.
-	if test "$cputype" = "386"; then
+	if test "$cputype" = 386; then
 	    UNAME_MACHINE=i386
 	else
 	    UNAME_MACHINE="$cputype"
 	fi
 	echo ${UNAME_MACHINE}-unknown-plan9
 	exit ;;
     *:TOPS-10:*:*)
 	echo pdp10-unknown-tops10
@@ -1364,28 +1395,31 @@ EOF
 	echo ${UNAME_MACHINE}-pc-rdos
 	exit ;;
     i*86:AROS:*:*)
 	echo ${UNAME_MACHINE}-pc-aros
 	exit ;;
     x86_64:VMkernel:*:*)
 	echo ${UNAME_MACHINE}-unknown-esx
 	exit ;;
+    amd64:Isilon\ OneFS:*:*)
+	echo x86_64-unknown-onefs
+	exit ;;
 esac
 
 cat >&2 <<EOF
 $0: unable to guess system type
 
 This script, last modified $timestamp, has failed to recognize
 the operating system you are using. It is advised that you
 download the most up to date version of the config scripts from
 
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
 and
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
 If the version you run ($0) is already up to date, please
 send the following data and any information you think might be
 pertinent to <config-patches@gnu.org> in order to provide the needed
 information to handle your system.
 
 config.guess timestamp = $timestamp
 
--- a/build/autoconf/config.sub
+++ b/build/autoconf/config.sub
@@ -1,13 +1,13 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2014 Free Software Foundation, Inc.
+#   Copyright 1992-2016 Free Software Foundation, Inc.
 
-timestamp='2014-01-01'
+timestamp='2016-03-30'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,25 +20,25 @@ timestamp='2014-01-01'
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # configuration script generated by Autoconf, you may include it under
 # the same distribution terms that you use for the rest of that
 # program.  This Exception is an additional permission under section 7
 # of the GNU General Public License, version 3 ("GPLv3").
 
 
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
 # If it is invalid, we print an error message on stderr and exit with code 1.
 # Otherwise, we print the canonical config type on stdout and succeed.
 
 # You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
 # This file is supposed to be the same for all GNU packages
 # and recognize all the CPU types, system types and aliases
 # that are meaningful with *any* GNU software.
 # Each package is responsible for reporting which valid configurations
 # it does not support.  The user should be able to distinguish
 # a failure to support a valid configuration from a meaningless
 # configuration.
@@ -48,32 +48,31 @@ timestamp='2014-01-01'
 #	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
 # or in some cases, the newer four-part form:
 #	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
 # It is wrong to echo any other type of specification.
 
 me=`echo "$0" | sed -e 's,.*/,,'`
 
 usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
-       $0 [OPTION] ALIAS
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
 
 Canonicalize a configuration name.
 
 Operation modes:
   -h, --help         print this help, then exit
   -t, --time-stamp   print date of last modification, then exit
   -v, --version      print version number, then exit
 
 Report bugs and patches to <config-patches@gnu.org>."
 
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2014 Free Software Foundation, Inc.
+Copyright 1992-2016 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 
 help="
 Try \`$me --help' for more information."
 
 # Parse command line
@@ -110,24 +109,28 @@ case $# in
  *) echo "$me: too many arguments$help" >&2
     exit 1;;
 esac
 
 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
 # Here we must recognize all the valid KERNEL-OS combinations.
 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
-  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | \
+  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
-  knetbsd*-gnu* | netbsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
     ;;
+  android-linux)
+    os=-linux-android
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    ;;
   *)
     basic_machine=`echo $1 | sed 's/-[^-]*$//'`
     if [ $basic_machine != $1 ]
     then os=`echo $1 | sed 's/.*-/-/'`
     else os=; fi
     ;;
 esac
 
@@ -246,22 +249,23 @@ case $basic_machine in
 	| a29k \
 	| aarch64 | aarch64_be \
 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
 	| am33_2.0 \
 	| arc | arceb \
 	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
 	| avr | avr32 \
+	| ba \
 	| be32 | be64 \
 	| bfin \
 	| c4x | c8051 | clipper \
 	| d10v | d30v | dlx | dsp16xx \
-	| epiphany \
-	| fido | fr30 | frv \
+	| e2k | epiphany \
+	| fido | fr30 | frv | ft32 \
 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
 	| hexagon \
 	| i370 | i860 | i960 | ia64 \
 	| ip2k | iq2000 \
 	| k1om \
 	| le32 | le64 \
 	| lm32 \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
@@ -274,58 +278,64 @@ case $basic_machine in
 	| mips64r5900 | mips64r5900el \
 	| mips64vr | mips64vrel \
 	| mips64vr4100 | mips64vr4100el \
 	| mips64vr4300 | mips64vr4300el \
 	| mips64vr5000 | mips64vr5000el \
 	| mips64vr5900 | mips64vr5900el \
 	| mipsisa32 | mipsisa32el \
 	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
 	| mipsisa64 | mipsisa64el \
 	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
 	| mipsisa64sb1 | mipsisa64sb1el \
 	| mipsisa64sr71k | mipsisa64sr71kel \
 	| mipsr5900 | mipsr5900el \
 	| mipstx39 | mipstx39el \
 	| mn10200 | mn10300 \
 	| moxie \
 	| mt \
 	| msp430 \
 	| nds32 | nds32le | nds32be \
 	| nios | nios2 | nios2eb | nios2el \
 	| ns16k | ns32k \
-	| open8 \
-	| or1k | or32 \
+	| open8 | or1k | or1knd | or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| powerpc | powerpc64 | powerpc64le | powerpcle \
 	| pyramid \
+	| riscv32 | riscv64 \
 	| rl78 | rx \
 	| score \
-	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
 	| sh64 | sh64le \
 	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
 	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
 	| spu \
 	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
 	| ubicom32 \
 	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| visium \
 	| we32k \
 	| x86 | xc16x | xstormy16 | xtensa \
 	| z8k | z80)
 		basic_machine=$basic_machine-unknown
 		;;
 	c54x)
 		basic_machine=tic54x-unknown
 		;;
 	c55x)
 		basic_machine=tic55x-unknown
 		;;
 	c6x)
 		basic_machine=tic6x-unknown
 		;;
+	leon|leon[3-9])
+		basic_machine=sparc-$basic_machine
+		;;
 	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
 		basic_machine=$basic_machine-unknown
 		os=-none
 		;;
 	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
 		;;
 	ms1)
 		basic_machine=mt-unknown
@@ -361,22 +371,23 @@ case $basic_machine in
 	580-* \
 	| a29k-* \
 	| aarch64-* | aarch64_be-* \
 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| avr-* | avr32-* \
+	| ba-* \
 	| be32-* | be64-* \
 	| bfin-* | bs2000-* \
 	| c[123]* | c30-* | [cjt]90-* | c4x-* \
 	| c8051-* | clipper-* | craynv-* | cydra-* \
 	| d10v-* | d30v-* | dlx-* \
-	| elxsi-* \
+	| e2k-* | elxsi-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
 	| h8300-* | h8500-* \
 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
 	| hexagon-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| ip2k-* | iq2000-* \
 	| k1om-* \
 	| le32-* | le64-* \
@@ -393,46 +404,51 @@ case $basic_machine in
 	| mips64r5900-* | mips64r5900el-* \
 	| mips64vr-* | mips64vrel-* \
 	| mips64vr4100-* | mips64vr4100el-* \
 	| mips64vr4300-* | mips64vr4300el-* \
 	| mips64vr5000-* | mips64vr5000el-* \
 	| mips64vr5900-* | mips64vr5900el-* \
 	| mipsisa32-* | mipsisa32el-* \
 	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
 	| mipsisa64-* | mipsisa64el-* \
 	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
 	| mipsisa64sb1-* | mipsisa64sb1el-* \
 	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
 	| mipsr5900-* | mipsr5900el-* \
 	| mipstx39-* | mipstx39el-* \
 	| mmix-* \
 	| mt-* \
 	| msp430-* \
 	| nds32-* | nds32le-* | nds32be-* \
 	| nios-* | nios2-* | nios2eb-* | nios2el-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
 	| open8-* \
+	| or1k*-* \
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
 	| pyramid-* \
+	| riscv32-* | riscv64-* \
 	| rl78-* | romp-* | rs6000-* | rx-* \
 	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
 	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
 	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
 	| sparclite-* \
-	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
 	| tahoe-* \
 	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
 	| tile*-* \
 	| tron-* \
 	| ubicom32-* \
 	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
 	| vax-* \
+	| visium-* \
 	| we32k-* \
 	| x86-* | x86_64-* | xc16x-* | xps100-* \
 	| xstormy16-* | xtensa*-* \
 	| ymp-* \
 	| z8k-* | z80-*)
 		;;
 	# Recognize the basic CPU types without company name, with glob match.
 	xtensa*)
@@ -499,16 +515,19 @@ case $basic_machine in
 	apollo68bsd)
 		basic_machine=m68k-apollo
 		os=-bsd
 		;;
 	aros)
 		basic_machine=i386-pc
 		os=-aros
 		;;
+	asmjs)
+		basic_machine=asmjs-unknown
+		;;
 	aux)
 		basic_machine=m68k-apple
 		os=-aux
 		;;
 	balance)
 		basic_machine=ns32k-sequent
 		os=-dynix
 		;;
@@ -760,16 +779,19 @@ case $basic_machine in
 			os=-irix4
 			;;
 		esac
 		;;
 	isi68 | isi)
 		basic_machine=m68k-isi
 		os=-sysv
 		;;
+	leon-*|leon[3-9]-*)
+		basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+		;;
 	m68knommu)
 		basic_machine=m68k-unknown
 		os=-linux
 		;;
 	m68knommu-*)
 		basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
 		os=-linux
 		;;
@@ -815,16 +837,20 @@ case $basic_machine in
 	monitor)
 		basic_machine=m68k-rom68k
 		os=-coff
 		;;
 	morphos)
 		basic_machine=powerpc-unknown
 		os=-morphos
 		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
 	msdos)
 		basic_machine=i386-pc
 		os=-msdos
 		;;
 	ms1-*)
 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
 		;;
 	msys)
@@ -1347,37 +1373,38 @@ case $os in
 	# The portable systems comes first.
 	# Each alternative MUST END IN A *, to match a version number.
 	# -sysv* is not here because it comes later, after sysvr4.
 	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
 	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
 	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
 	      | -sym* | -kopensolaris* | -plan9* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-	      | -aos* | -aros* \
+	      | -aos* | -aros* | -cloudabi* | -sortix* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
-	      | -bitrig* | -openbsd* | -solidbsd* \
+	      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
 	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
 	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
 	      | -chorusos* | -chorusrdb* | -cegcc* \
 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-	      | -mingw32* | -mingw64* | -linux-gnu* \
+	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
 	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
-	      | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
+	      | -onefs* | -tirtos*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 	-qnx*)
 		case $basic_machine in
 		    x86-* | i*86-*)
 			;;
 		    *)
 			os=-nto$os
@@ -1497,21 +1524,20 @@ case $os in
 		os=-aros
 		;;
 	-zvmoe)
 		os=-zvmoe
 		;;
 	-dicos*)
 		os=-dicos
 		;;
-        -android*)
-	        os=-android
-                ;;
 	-nacl*)
 		;;
+	-ios)
+		;;
 	-none)
 		;;
 	*)
 		# Get rid of the `-' at the beginning of $os.
 		os=`echo $os | sed 's/[^-]*-//'`
 		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
 		exit 1
 		;;
@@ -1588,19 +1614,16 @@ case $basic_machine in
 		os=-elf
 		;;
 	mips*-cisco)
 		os=-elf
 		;;
 	mips*-*)
 		os=-elf
 		;;
-	or1k-*)
-		os=-elf
-		;;
 	or32-*)
 		os=-coff
 		;;
 	*-tti)	# must be before sparc entry or we get the wrong os.
 		os=-sysv3
 		;;
 	sparc-* | *-sun)
 		os=-sunos4.1.1
@@ -1771,19 +1794,16 @@ case $basic_machine in
 				vendor=apple
 				;;
 			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
 				vendor=atari
 				;;
 			-vos*)
 				vendor=stratus
 				;;
-			*-android*|*-linuxandroid*)
-				vendor=linux-
-				;;
 		esac
 		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
 		;;
 esac
 
 echo $basic_machine$os
 exit
 
--- a/build/autoconf/hotfixes.m4
+++ b/build/autoconf/hotfixes.m4
@@ -11,14 +11,13 @@ dnl prints is replaced with:
 dnl   #define posix_memalign innocuous_posix_memalign
 dnl   #include "theinclude"
 dnl   #undef posix_memalign
 dnl This avoids double declaration of that function when the header normally
 dnl declares it, while the test itself is just expecting the function not to be
 dnl declared at all, and declares it differently (which doesn't matter for the
 dnl test itself).
 dnl More recent versions of autoconf are essentially doing this.
-define([ac_cv_func_], [ac_cv_func2_])dnl
 define([_AC_CHECK_FUNC],defn([AC_CHECK_FUNC]))dnl
 define([AC_CHECK_FUNC], [dnl
 patsubst(_AC_CHECK_FUNC($@), [#include.*$], [#define $1 innocuous_$1
 \&
 #undef $1])])dnl
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -130,25 +130,16 @@ fi
 AC_MSG_RESULT([$HOST_CC])
 AC_MSG_CHECKING([for host c++ compiler])
 AC_CHECK_PROGS(HOST_CXX, c++ g++ clang++ cl, "")
 if test -z "$HOST_CXX"; then
     AC_MSG_ERROR([no acceptable c++ compiler found in \$PATH])
 fi
 AC_MSG_RESULT([$HOST_CXX])
 
-if test -z "$HOST_CFLAGS"; then
-    HOST_CFLAGS="$CFLAGS"
-fi
-if test -z "$HOST_CXXFLAGS"; then
-    HOST_CXXFLAGS="$CXXFLAGS"
-fi
-if test -z "$HOST_LDFLAGS"; then
-    HOST_LDFLAGS="$LDFLAGS"
-fi
 if test -z "$HOST_AR_FLAGS"; then
     HOST_AR_FLAGS="$AR_FLAGS"
 fi
 AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :)
 AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :)
 CC="$HOST_CC"
 CFLAGS="$HOST_CFLAGS"
 LDFLAGS="$HOST_LDFLAGS"
@@ -164,38 +155,43 @@ AC_MSG_CHECKING([whether the host c++ co
 AC_TRY_COMPILE([], [return(0);],
     [ac_cv_prog_hostcxx_works=1 AC_MSG_RESULT([yes])],
     AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CXX cannot create executables.]) )
 
 CC=$_SAVE_CC
 CFLAGS=$_SAVE_CFLAGS
 LDFLAGS=$_SAVE_LDFLAGS
 
-AC_CHECK_PROGS(CC, "${target_alias}-gcc" "${target}-gcc", :)
+dnl AC_CHECK_PROGS manually goes through $PATH, and as such fails to handle
+dnl absolute or relative paths. Relative paths wouldn't work anyways, but
+dnl absolute paths would. Trick AC_CHECK_PROGS into working in that case by
+dnl adding / to PATH. This is temporary until this moves to moz.configure
+dnl (soon).
+_SAVE_PATH=$PATH
+case "${TOOLCHAIN_PREFIX}" in
+/*)
+    PATH="/:$PATH"
+    ;;
+esac
+AC_CHECK_PROGS(CC, "${TOOLCHAIN_PREFIX}gcc", :)
 unset ac_cv_prog_CC
 AC_PROG_CC
-AC_CHECK_PROGS(CXX, "${target_alias}-g++" "${target}-g++", :)
+AC_CHECK_PROGS(CXX, "${TOOLCHAIN_PREFIX}g++", :)
 unset ac_cv_prog_CXX
 AC_PROG_CXX
 
-AC_CHECK_PROGS(RANLIB, "${target_alias}-ranlib" "${target}-ranlib", :)
-AC_CHECK_PROGS(AR, "${target_alias}-ar" "${target}-ar", :)
-AC_CHECK_PROGS(AS, "${target_alias}-as" "${target}-as", :)
-AC_CHECK_PROGS(LD, "${target_alias}-ld" "${target}-ld", :)
-AC_CHECK_PROGS(STRIP, "${target_alias}-strip" "${target}-strip", :)
-AC_CHECK_PROGS(WINDRES, "${target_alias}-windres" "${target}-windres", :)
-AC_CHECK_PROGS(OTOOL, "${target_alias}-otool" "${target}-otool", :)
-AC_DEFINE(CROSS_COMPILE)
-CROSS_COMPILE=1
-
-dnl If we cross compile for ppc on Mac OS X x86, cross_compiling will
-dnl dnl have erroneously been set to "no", because the x86 build host is
-dnl dnl able to run ppc code in a translated environment, making a cross
-dnl dnl compiler appear native.  So we override that here.
-cross_compiling=yes
+AC_CHECK_PROGS(RANLIB, "${TOOLCHAIN_PREFIX}ranlib", :)
+AC_CHECK_PROGS(AR, "${TOOLCHAIN_PREFIX}ar", :)
+AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :)
+AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :)
+AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :)
+AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :)
+AC_CHECK_PROGS(OTOOL, "${TOOLCHAIN_PREFIX}otool", :)
+AC_CHECK_PROGS(OBJCOPY, "${TOOLCHAIN_PREFIX}objcopy", :)
+PATH=$_SAVE_PATH
 ])
 
 AC_DEFUN([MOZ_CXX11],
 [
 dnl Check whether gcc's c++0x mode works
 dnl Updates to the test below should be duplicated further below for the
 dnl cross-compiling case.
 AC_LANG_CPLUSPLUS
--- a/build/macosx/cross-mozconfig.common
+++ b/build/macosx/cross-mozconfig.common
@@ -20,21 +20,16 @@ CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/
 FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
 
 export CC="$topsrcdir/clang/bin/clang $FLAGS"
 export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
 export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
 export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
 export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
 export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
-#TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically
-export AR=${TOOLCHAIN_PREFIX}ar
-export RANLIB=${TOOLCHAIN_PREFIX}ranlib
-export STRIP=${TOOLCHAIN_PREFIX}strip
-export OTOOL=${TOOLCHAIN_PREFIX}otool
 export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
 export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage
 export DMG_TOOL=$topsrcdir/dmg/dmg
 
 export HOST_CC="$topsrcdir/clang/bin/clang"
 export HOST_CXX="$topsrcdir/clang/bin/clang++"
 export HOST_CPP="$topsrcdir/clang/bin/clang -E"
 export HOST_CFLAGS="-g"
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -284,16 +284,17 @@ def wanted_mozconfig_variables(help):
          'MOZ_JPROF',
          'MOZ_PROFILING',
          'MOZ_USE_SYSTRACE',
          'MOZ_VTUNE',
          'MOZTTDIR',
          'PERL',
          'RPMBUILD',
          'TAR',
+         'TOOLCHAIN_PREFIX',
          'UNZIP',
          'USE_FC_FREETYPE',
          'WITHOUT_X',
          'XARGS',
          'YASM',
          'ZIP',
      ])
 
@@ -402,46 +403,68 @@ def split_triplet(triplet):
         canonical_os = canonical_kernel = 'OpenBSD'
     else:
         die('Unknown OS: %s' % os)
 
     # The CPU granularity is probably not enough. Moving more things from
     # old-configure will tell us if we need more
     if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu):
         canonical_cpu = 'x86'
-    elif cpu in ('s390', 's390x', 'x86_64', 'ia64'):
+        endianness = 'little'
+    elif cpu in ('x86_64', 'ia64'):
         canonical_cpu = cpu
+        endianness = 'little'
+    elif cpu in ('s390', 's390x'):
+        canonical_cpu = cpu
+        endianness = 'big'
     elif cpu in ('powerpc64', 'ppc64', 'powerpc64le', 'ppc64le'):
         canonical_cpu = 'ppc64'
+        endianness = 'little' if 'le' in cpu else 'big'
     elif cpu in ('powerpc', 'ppc', 'rs6000') or cpu.startswith('powerpc'):
         canonical_cpu = 'ppc'
+        endianness = 'big'
     elif cpu in ('Alpha', 'alpha', 'ALPHA'):
         canonical_cpu = 'Alpha'
+        endianness = 'little'
     elif cpu.startswith('hppa') or cpu == 'parisc':
         canonical_cpu = 'hppa'
+        endianness = 'big'
+    elif cpu.startswith('sparc64'):
+        canonical_cpu = 'sparc64'
+        endianness = 'big'
     elif cpu.startswith('sparc') or cpu == 'sun4u':
         canonical_cpu = 'sparc'
+        endianness = 'big'
     elif cpu.startswith('arm'):
         canonical_cpu = 'arm'
+        endianness = 'big' if cpu.startswith(('armeb', 'armbe')) else 'little'
     elif cpu in ('mips', 'mipsel'):
         canonical_cpu = 'mips32'
+        endianness = 'little' if 'le' in cpu else 'big'
     elif cpu in ('mips64', 'mips64el'):
         canonical_cpu = 'mips64'
+        endianness = 'little' if 'le' in cpu else 'big'
     elif cpu.startswith('aarch64'):
         canonical_cpu = 'aarch64'
+        endianness = 'little'
     else:
         canonical_cpu = cpu
+        endianness = 'unknown'
 
     return namespace(
         alias=triplet,
         cpu=canonical_cpu,
         kernel=canonical_kernel,
         os=canonical_os,
+        endianness=endianness,
         raw_cpu=cpu,
         raw_os=os,
+        # Toolchains, most notably for cross compilation may use cpu-os
+        # prefixes.
+        toolchain='%s-%s' % (cpu, os),
     )
 
 
 @imports('subprocess')
 def config_sub(shell, triplet):
     config_sub = os.path.join(os.path.dirname(__file__), '..',
                               'autoconf', 'config.sub')
     return subprocess.check_output([shell, config_sub, triplet]).strip()
@@ -464,16 +487,26 @@ def host(value, shell):
 @depends('--target', host, shell)
 @checking('for target system type', lambda t: t.alias)
 def target(value, host, shell):
     if not value:
         return host
     return split_triplet(config_sub(shell, value[0]))
 
 
+@depends(host, target)
+@checking('whether cross compiling')
+def cross_compiling(host, target):
+    return host != target
+
+set_config('CROSS_COMPILE', cross_compiling)
+set_define('CROSS_COMPILE', cross_compiling)
+add_old_configure_assignment('CROSS_COMPILE', cross_compiling)
+
+
 # Autoconf needs these set
 @depends(host)
 def host_for_old_configure(host):
     return '--host=%s' % host.alias
 
 add_old_configure_arg(host_for_old_configure)
 
 @depends(host, target)
@@ -683,17 +716,18 @@ add_old_configure_assignment('NIGHTLY_BU
                              delayed_getattr(milestone, 'is_nightly'))
 set_config('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 set_define('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 add_old_configure_assignment('RELEASE_BUILD',
                              delayed_getattr(milestone, 'is_release'))
 
 
 # This is temporary until js/src/configure and configure are merged.
-# Use instead of option() in js/moz.configure
+# Use instead of option() in js/moz.configure and more generally, for
+# options that are shared between configure and js/src/configure.
 @template
 def js_option(*args, **kwargs):
     opt = option(*args, **kwargs)
 
     @depends(opt.option, build_project)
     def js_option(value, build_project):
         if build_project != 'js':
             return value.format(opt.option)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -126,35 +126,40 @@ def prepare_configure(old_configure, moz
         # This could be done with a m4 macro, but it's way easier this way
         script = script.replace('>./config.log', '>>./config.log')
 
         with open(old_configure, 'wb') as fh:
             fh.write(script)
 
     cmd = [shell, old_configure]
     with encoded_open('old-configure.vars', 'w') as out:
+        log.debug('Injecting the following to old-configure:')
+        def inject(command):
+            print(command, file=out)
+            log.debug('| %s', command)
+
         if mozconfig['path']:
             for key, value in mozconfig['env']['added'].items():
-                print("export %s=%s" % (key, quote(value)), file=out)
+                inject("export %s=%s" % (key, quote(value)))
             for key, (old, value) in mozconfig['env']['modified'].items():
-                print("export %s=%s" % (key, quote(value)), file=out)
+                inject("export %s=%s" % (key, quote(value)))
             for key, value in mozconfig['vars']['added'].items():
-                print("%s=%s" % (key, quote(value)), file=out)
+                inject("%s=%s" % (key, quote(value)))
             for key, (old, value) in mozconfig['vars']['modified'].items():
-                print("%s=%s" % (key, quote(value)), file=out)
+                inject("%s=%s" % (key, quote(value)))
             for t in ('env', 'vars'):
                 for key in mozconfig[t]['removed'].keys():
-                    print("unset %s" % key, file=out)
+                    inject("unset %s" % key)
 
         # Autoconf is special, because it might be passed from
         # mozconfig['make_extra'], which we don't pass automatically above.
-        print('export AUTOCONF=%s' % quote(autoconf), file=out)
+        inject('export AUTOCONF=%s' % quote(autoconf))
 
         for assignment in old_configure_assignments:
-            print(assignment, file=out)
+            inject(assignment)
 
     return cmd
 
 
 @template
 def old_configure_options(*options):
     for opt in options:
         option(opt, nargs='*', help='Help missing for old configure options')
@@ -315,17 +320,16 @@ def old_configure_options(*options):
     '--with-debug-label',
     '--with-default-mozilla-five-home',
     '--with-distribution-id',
     '--with-doc-include-dirs',
     '--with-doc-input-dirs',
     '--with-doc-output-dir',
     '--with-float-abi',
     '--with-fpu',
-    '--with-gonk-toolchain-prefix',
     '--with-google-api-keyfile',
     '--with-google-oauth-api-keyfile',
     '--with-intl-api',
     '--with-ios-sdk',
     '--with-java-bin-path',
     '--with-jitreport-granularity',
     '--with-linux-headers',
     '--with-macbundlename-prefix',
@@ -389,17 +393,17 @@ def old_configure(prepare_configure, ext
     # We also pass it the options from js/moz.configure so that it can pass
     # them down to js/src/configure. Note this list is empty when running
     # js/src/configure, in which case we don't need to pass those options
     # to old-configure since old-configure doesn't handle them anyways.
     if extra_old_configure_args:
         cmd += extra_old_configure_args
 
     # For debugging purpose, in case it's not what we'd expect.
-    log.info('running %s', ' '.join(quote(a) for a in cmd))
+    log.debug('Running %s', ' '.join(quote(a) for a in cmd))
 
     # Our logging goes to config.log, the same file old.configure uses.
     # We can't share the handle on the file, so close it. We assume nothing
     # beyond this point is going to be interesting to log to config.log from
     # our end, so we don't make the effort to recreate a logging.FileHandler.
     logger = logging.getLogger('moz.configure')
     for handler in logger.handlers:
         if isinstance(handler, logging.FileHandler):
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -96,8 +96,27 @@ def compiler_wrapper(wrapper, ccache):
 
 add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper)
 
 @depends_if(compiler_wrapper)
 def using_compiler_wrapper(compiler_wrapper):
     return True
 
 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
+
+
+# Cross-compilation related things.
+# ==============================================================
+js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
+          help='Prefix for the target toolchain')
+
+@depends('--with-toolchain-prefix', target, host, cross_compiling)
+def toolchain_prefix(value, target, host, cross_compiling):
+    if value:
+        return value[0]
+    # Special case x86-64 <-> x86 cross compiling until we have the right tests
+    # in moz.configure land.
+    if cross_compiling and not all(i.cpu in ('x86_64', 'x86')
+                                   for i in (target, host)):
+        return '%s-' % target.toolchain
+    return ''
+
+add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -855,30 +855,30 @@ mk_libname = $(basename lib$(notdir $1))
 src_libdep = $(call mk_libname,$1): $1 $$(call mkdir_deps,$$(MDDEPDIR))
 $(foreach f,$(RSSRCS),$(eval $(call src_libdep,$(f))))
 
 $(OBJS) $(HOST_OBJS) $(PROGOBJS) $(HOST_PROGOBJS): $(GLOBAL_DEPS)
 
 # Rules for building native targets must come first because of the host_ prefix
 $(HOST_COBJS):
 	$(REPORT_BUILD)
-	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
+	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CPPOBJS):
 	$(REPORT_BUILD)
 	$(call BUILDSTATUS,OBJECT_FILE $@)
-	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
+	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CMOBJS):
 	$(REPORT_BUILD)
-	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
+	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CMMOBJS):
 	$(REPORT_BUILD)
-	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
+	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(COBJS):
 	$(REPORT_BUILD)
 	$(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
 # DEFINES and ACDEFINES are needed here to enable conditional compilation of Q_OBJECTs:
 # 'moc' only knows about #defines it gets on the command line (-D...), not in
 # included headers like mozilla-config.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -345,17 +345,17 @@ ForEachPing(nsIContent* aContent, ForEac
   //       implemented an interface that exposed an enumeration of nsIURIs.
 
   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
   // or XHTML namespace.
   if (!IsElementAnchor(aContent)) {
     return;
   }
 
-  nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
+  nsCOMPtr<nsIAtom> pingAtom = NS_Atomize("ping");
   if (!pingAtom) {
     return;
   }
 
   nsAutoString value;
   aContent->GetAttr(kNameSpaceID_None, pingAtom, value);
   if (value.IsEmpty()) {
     return;
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -156,17 +156,17 @@ TimingParams::ParseEasing(const nsAStrin
     case eCSSUnit_Unset:
     case eCSSUnit_TokenStream:
     case eCSSUnit_Null:
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
       break;
   }
-  aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>();
+  aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
   return Nothing();
 }
 
 bool
 TimingParams::operator==(const TimingParams& aOther) const
 {
   return mDuration == aOther.mDuration &&
          mDelay == aOther.mDelay &&
--- a/dom/animation/TimingParams.h
+++ b/dom/animation/TimingParams.h
@@ -55,17 +55,18 @@ struct TimingParams
       double durationInMs = aDuration.GetAsUnrestrictedDouble();
       if (durationInMs >= 0) {
         result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
       } else {
         aRv.ThrowTypeError<dom::MSG_ENFORCE_RANGE_OUT_OF_RANGE>(
           NS_LITERAL_STRING("duration"));
       }
     } else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
-      aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>();
+      aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>(
+        aDuration.GetAsString());
     }
     return result;
   }
 
   static void ValidateIterationStart(double aIterationStart,
                                      ErrorResult& aRv)
   {
     if (aIterationStart < 0) {
--- a/dom/animation/test/chrome/test_animate_xrays.html
+++ b/dom/animation/test/chrome/test_animate_xrays.html
@@ -14,17 +14,17 @@
 <script>
 'use strict';
 
 var win = document.getElementById('iframe').contentWindow;
 
 async_test(function(t) {
   window.addEventListener('load', t.step_func(function() {
     var target = win.document.getElementById('target');
-    var anim = target.animate({ opacity: [ 0, 1 ] }, 2000);
+    var anim = target.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
     // In the x-ray case, the frames object will be given an opaque wrapper
     // so it won't be possible to fetch any frames from it.
     assert_equals(anim.effect.getFrames().length, 0);
     t.done();
   }));
 }, 'Calling animate() across x-rays');
 
 </script>
--- a/dom/animation/test/chrome/test_animation_observers.html
+++ b/dom/animation/test/chrome/test_animation_observers.html
@@ -1,13 +1,14 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Test chrome-only MutationObserver animation notifications</title>
 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+<script src="../testcommon.js"></script>
 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 <style>
 @keyframes anim {
   to { transform: translate(100px); }
 }
 #target {
   width: 100px;
   height: 100px;
@@ -323,17 +324,17 @@ function assert_records(expected, desc) 
     // be delivered.
     yield await_frame();
     assert_records([{ added: [firstAnimation], changed: [], removed: [] }],
                    "records after transition start");
 
     // Wait for the Animation to be playing, then seek well into
     // the transition.
     yield firstAnimation.ready;
-    firstAnimation.currentTime = 50000;
+    firstAnimation.currentTime = 50 * MS_PER_SEC;
 
     // Reverse the transition by setting the background-color back to its
     // original value.
     e.style.backgroundColor = "yellow";
 
     // The reversal should cause the creation of a new Animation.
     animations = e.getAnimations();
     is(animations.length, 1, "getAnimations().length after transition reversal");
@@ -383,17 +384,17 @@ function assert_records(expected, desc) 
                    "records after transition starts");
 
     // Wait for the Animations to get going.
     yield animations[0].ready;
     is(animations.filter(p => p.playState == "running").length, 3,
        "number of running Animations");
 
     // Seek well into each animation.
-    animations.forEach(p => p.currentTime = 50000);
+    animations.forEach(p => p.currentTime = 50 * MS_PER_SEC);
 
     // Prepare the set of expected change MutationRecords, one for each
     // animation that was seeked.
     var seekRecords = animations.map(
       p => ({ added: [], changed: [p], removed: [] })
     );
 
     // Cancel one of the transitions by setting transition-property.
@@ -506,17 +507,17 @@ function assert_records(expected, desc) 
 
     // Wait for the single MutationRecord for the Animation addition to
     // be delivered.
     yield await_frame();
     assert_records([{ added: animations, changed: [], removed: [] }],
                    "records after animation start");
 
     // Advance the animation by a second.
-    animations[0].currentTime += 1000;
+    animations[0].currentTime += 1 * MS_PER_SEC;
 
     // Cancel the animation by setting animation-duration to a value less
     // than a second.
     e.style.animationDuration = "0.1s";
 
     // Wait for the change MutationRecord from seeking the Animation to
     // be delivered, followed by a further MutationRecord for the Animation
     // removal.
@@ -612,17 +613,17 @@ function assert_records(expected, desc) 
 
     // Wait for the single MutationRecord for the Animation addition to
     // be delivered.
     yield await_frame();
     assert_records([{ added: animations, changed: [], removed: [] }],
                    "records after animation start");
 
     // Advance the animation until we are past the first iteration.
-    animations[0].currentTime += 1000;
+    animations[0].currentTime += 1 * MS_PER_SEC;
 
     // The only MutationRecord at this point should be the change from
     // seeking the Animation.
     yield await_frame();
     assert_records([{ added: [], changed: animations, removed: [] }],
                    "records after seeking animations");
 
     // Cancel the animation by setting animation-iteration-count.
@@ -719,18 +720,18 @@ function assert_records(expected, desc) 
       e.style = "";
     });
   });
 
   // Test that updating a property on the Animation object dispatches a changed
   // notification.
   [
     { prop: "playbackRate", val: 0.5 },
-    { prop: "startTime",    val: 50000 },
-    { prop: "currentTime",  val: 50000 },
+    { prop: "startTime",    val: 50 * MS_PER_SEC },
+    { prop: "currentTime",  val: 50 * MS_PER_SEC },
   ].forEach(function(aChangeTest) {
     addAsyncAnimTest(`single_animation_api_change_${aChangeTest.prop}`,
                      aOptions, function*() {
       // Start a long animation
       //
       // We use a forwards fill mode so that even if the change we make causes
       // the animation to become finished, it will still be "relevant" so we
       // won't mark it as removed.
@@ -882,17 +883,17 @@ function assert_records(expected, desc) 
 
     // Wait for the single MutationRecord for the Animation addition to
     // be delivered.
     yield await_frame();
     assert_records([{ added: animations, changed: [], removed: [] }],
                     "records after animation start");
 
     // Seek to the middle and set playbackRate to zero.
-    animations[0].currentTime = 50000;
+    animations[0].currentTime = 50 * MS_PER_SEC;
     animations[0].playbackRate = 0;
 
     // Wait for the MutationRecords, one for each change, to be delivered.
     yield await_frame();
     assert_records([{ added: [], changed: animations, removed: [] },
                     { added: [], changed: animations, removed: [] }],
                     "records after seeking and setting playbackRate");
 
@@ -1519,37 +1520,37 @@ addAsyncAnimTest("tree_ordering", { obse
   childA.remove();
   childB.remove();
   extraStyle.remove();
 });
 
 [ div, pseudoTarget ].forEach(function(target) {
   addAsyncAnimTest("change_duration_and_currenttime",
                    { observe: div, subtree: true }, function*() {
-    var anim = target.animate({ opacity: [ 0, 1 ] }, 100000);
+    var anim = target.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
 
     yield await_frame();
     assert_records([{ added: [anim], changed: [], removed: [] }],
                    "records after animation is added");
 
-    anim.effect.timing.duration = 10000;
+    anim.effect.timing.duration = 100 * MS_PER_SEC;
     yield await_frame();
     assert_records([{ added: [], changed: [anim], removed: [] }],
                    "records after duration is changed");
 
-    anim.effect.timing.duration = 10000;
+    anim.effect.timing.duration = 100 * MS_PER_SEC;
     yield await_frame();
     assert_records([], "records after assigning same value");
 
-    anim.currentTime = 50000;
+    anim.currentTime = 500 * MS_PER_SEC;
     yield await_frame();
     assert_records([{ added: [], changed: [], removed: [anim] }],
                    "records after animation end");
 
-    anim.effect.timing.duration = 100000;
+    anim.effect.timing.duration = 1000 * MS_PER_SEC;
     yield await_frame();
     assert_records([{ added: [anim], changed: [], removed: [] }],
                    "records after animation restarted");
 
     anim.effect.timing.duration = "auto";
     yield await_frame();
     assert_records([{ added: [], changed: [], removed: [anim] }],
                    "records after duration set \"auto\"");
@@ -1560,55 +1561,56 @@ addAsyncAnimTest("tree_ordering", { obse
 
     anim.cancel();
     yield await_frame();
   });
 });
 
 addAsyncAnimTest("change_enddelay_and_currenttime",
                  { observe: div, subtree: true }, function*() {
-  var anim = div.animate({ opacity: [ 0, 1 ] }, { duration: 100000 });
+  var anim = div.animate({ opacity: [ 0, 1 ] }, { duration: 100 * MS_PER_SEC });
 
   yield await_frame();
   assert_records([{ added: [anim], changed: [], removed: [] }],
                  "records after animation is added");
 
-  anim.effect.timing.endDelay = 10000;
+  anim.effect.timing.endDelay = 10 * MS_PER_SEC;
   yield await_frame();
   assert_records([{ added: [], changed: [anim], removed: [] }],
                  "records after endDelay is changed");
 
-  anim.effect.timing.endDelay = 10000;
+  anim.effect.timing.endDelay = 10 * MS_PER_SEC;
   yield await_frame();
   assert_records([], "records after assigning same value");
 
-  anim.currentTime = 109000;
+  anim.currentTime = 109 * MS_PER_SEC;
   yield await_frame();
   assert_records([{ added: [], changed: [], removed: [anim] }],
                  "records after currentTime during endDelay");
 
-  anim.effect.timing.endDelay = -110000;
+  anim.effect.timing.endDelay = -110 * MS_PER_SEC;
   yield await_frame();
   assert_records([], "records after assigning negative value");
 
   anim.cancel();
   yield await_frame();
 });
 
 addAsyncAnimTest("change_enddelay_and_currenttime",
                  { observe: div, subtree: true }, function*() {
   var anim = div.animate({ opacity: [ 0, 1 ] },
-                         { duration: 100, endDelay: -100 });
+                         { duration: 100 * MS_PER_SEC,
+                           endDelay: -100 * MS_PER_SEC });
   yield await_frame();
   assert_records([], "records after animation is added");
 });
 
 addAsyncAnimTest("change_iterations",
                  { observe: div, subtree: true }, function*() {
-  var anim = div.animate({ opacity: [ 0, 1 ] }, 100000);
+  var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   yield await_frame();
   assert_records([{ added: [anim], changed: [], removed: [] }],
                  "records after animation is added");
 
   anim.effect.timing.iterations = 2;
   yield await_frame();
   assert_records([{ added: [], changed: [anim], removed: [] }],
@@ -1631,18 +1633,20 @@ addAsyncAnimTest("change_iterations",
   anim.cancel();
   yield await_frame();
   assert_records([{ added: [], changed: [], removed: [anim] }],
                  "records after animation end");
 });
 
 addAsyncAnimTest("exclude_animations_targeting_pseudo_elements",
                  { observe: div, subtree: false }, function*() {
-  var anim = div.animate({ opacity: [ 0, 1 ] }, { duration: 100000 });
-  var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, { duration: 100000 });
+  var anim = div.animate({ opacity: [ 0, 1 ] },
+                         { duration: 100 * MS_PER_SEC });
+  var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
+                                   { duration: 100 * MS_PER_SEC });
 
   yield await_frame();
   assert_records([{ added: [anim], changed: [], removed: [] }],
                  "records after animation is added");
 
   anim.finish();
   pAnim.finish();
   yield await_frame();
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -210,18 +210,19 @@ var gAnimationsTests = [
         warning: 'AnimationWarningTransformWithGeometricProperties'
       }
     ]
   },
 ];
 
 gAnimationsTests.forEach(function(subtest) {
   promise_test(function(t) {
-    var div = addDiv(t, { class: 'compositable' });
-    var animation = div.animate(subtest.frames, 100000);
+    var animation = addDivAndAnimate(t,
+                                     { class: 'compositable' },
+                                     subtest.frames, 100 * MS_PER_SEC);
     return animation.ready.then(function() {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         subtest.expected);
     });
   }, subtest.desc);
 });
 
@@ -496,55 +497,57 @@ var gMultipleAsyncAnimationsWithGeometri
 function start() {
   var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
     .getService(SpecialPowers.Ci.nsIStringBundleService);
   gStringBundle = bundleService
     .createBundle("chrome://global/locale/layout_errors.properties");
 
   gAnimationsTests.forEach(function(subtest) {
     promise_test(function(t) {
-      var div = addDiv(t, { class: 'compositable' });
-      var animation = div.animate(subtest.frames, 100000);
+      var animation = addDivAndAnimate(t,
+                                       { class: 'compositable' },
+                                       subtest.frames, 100 * MS_PER_SEC);
       return animation.ready.then(function() {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected);
       });
     }, subtest.desc);
   });
 
   gPerformanceWarningTests.forEach(function(subtest) {
     promise_test(function(t) {
-      var div = addDiv(t, { class: 'compositable' });
-      var animation = div.animate(subtest.frames, 100000);
+      var animation = addDivAndAnimate(t,
+                                       { class: 'compositable' },
+                                       subtest.frames, 100 * MS_PER_SEC);
       return animation.ready.then(function() {
         assert_property_state_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
-        div.style = subtest.style;
+        animation.effect.target.style = subtest.style;
         return waitForFrame();
       }).then(function() {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected);
-        div.style = '';
+        animation.effect.target.style = '';
         return waitForFrame();
       }).then(function() {
         assert_property_state_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
       });
     }, subtest.desc);
   });
 
   gMultipleAsyncAnimationsTests.forEach(function(subtest) {
     promise_test(function(t) {
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(function(anim) {
-        var animation = div.animate(anim.frames, 100000);
+        var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
       return waitForAllAnimations(animations).then(function() {
         animations.forEach(function(anim) {
           assert_property_state_on_compositor(
@@ -570,17 +573,17 @@ function start() {
       });
     }, 'Multiple animations: ' + subtest.desc);
   });
 
   gMultipleAsyncAnimationsWithGeometricKeyframeTests.forEach(function(subtest) {
     promise_test(function(t) {
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(function(anim) {
-        var animation = div.animate(anim.frames, 100000);
+        var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
       return waitForAllAnimations(animations).then(function() {
         animations.forEach(function(anim) {
           assert_animation_property_state_equals(
@@ -590,34 +593,35 @@ function start() {
       });
     }, 'Multiple animations with geometric property: ' + subtest.desc);
   });
 
   gMultipleAsyncAnimationsWithGeometricAnimationTests.forEach(function(subtest) {
     promise_test(function(t) {
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(function(anim) {
-        var animation = div.animate(anim.frames, 100000);
+        var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
 
       var widthAnimation;
 
       return waitForAllAnimations(animations).then(function() {
         animations.forEach(function(anim) {
           assert_property_state_on_compositor(
             anim.effect.getProperties(),
             anim.expected);
         });
       }).then(function() {
         // Append 'width' animation on the same element.
-        widthAnimation = div.animate({ width: ['100px', '200px'] }, 100000);
+        widthAnimation = div.animate({ width: ['100px', '200px'] },
+                                     100 * MS_PER_SEC);
         return waitForFrame();
       }).then(function() {
         // Now transform animations are not running on compositor because of
         // the 'width' animation.
         animations.forEach(function(anim) {
           assert_animation_property_state_equals(
             anim.effect.getProperties(),
             anim.expected);
@@ -632,39 +636,41 @@ function start() {
             anim.effect.getProperties(),
             anim.expected);
         });
       });
     }, 'Multiple async animations and geometric animation: ' + subtest.desc);
   });
 
   promise_test(function(t) {
-    var div = addDiv(t, { class: 'compositable' });
-    var animation = div.animate(
-      { transform: ['translate(0px)', 'translate(100px)'] }, 100000);
+    var animation = addDivAndAnimate(t,
+                                     { class: 'compositable' },
+                                     { transform: [ 'translate(0px)',
+                                                    'translate(100px)'] },
+                                     100 * MS_PER_SEC);
     return animation.ready.then(function() {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
-      div.style = 'width: 10000px; height: 10000px';
+      animation.effect.target.style = 'width: 10000px; height: 10000px';
       return waitForFrame();
     }).then(function() {
       // viewport depends on test environment.
       var expectedWarning = new RegExp(
         "Async animation disabled because frame size \\(10000, 10000\\) is " +
         "bigger than the viewport \\(\\d+, \\d+\\) or the visual rectangle " +
         "\\(10000, 10000\\) is larger than the max allowed value \\(\\d+\\)");
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ {
           property: 'transform',
           runningOnCompositor: false,
           warning: expectedWarning
         } ]);
-      div.style = 'width: 100px; height: 100px';
+      animation.effect.target.style = 'width: 100px; height: 100px';
       return waitForFrame();
     }).then(function() {
       // FIXME: Bug 1253164: the animation should get back on compositor.
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: false } ]);
     });
   }, 'transform on too big element');
@@ -679,17 +685,17 @@ function start() {
     rect.setAttribute('fill', 'red');
     svg.appendChild(rect);
     document.body.appendChild(svg);
     t.add_cleanup(function() {
       svg.remove();
     });
 
     var animation = svg.animate(
-      { transform: ['translate(0px)', 'translate(100px)'] }, 100000);
+      { transform: ['translate(0px)', 'translate(100px)'] }, 100 * MS_PER_SEC);
     return animation.ready.then(function() {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
       svg.setAttribute('transform', 'translate(10, 20)');
       return waitForFrame();
     }).then(function() {
       assert_animation_property_state_equals(
@@ -707,17 +713,17 @@ function start() {
         [ { property: 'transform', runningOnCompositor: true } ]);
     });
   }, 'transform of nsIFrame with SVG transform');
 
   promise_test(function(t) {
     var div = addDiv(t, { class: 'compositable',
                           style: 'animation: fade 100s' });
     var cssAnimation = div.getAnimations()[0];
-    var scriptAnimation = div.animate({ opacity: [ 1, 0 ] }, 100000);
+    var scriptAnimation = div.animate({ opacity: [ 1, 0 ] }, 100 * MS_PER_SEC);
     return scriptAnimation.ready.then(function() {
       assert_animation_property_state_equals(
         cssAnimation.effect.getProperties(),
         [ { property: 'opacity', runningOnCompositor: false } ]);
       assert_animation_property_state_equals(
         scriptAnimation.effect.getProperties(),
         [ { property: 'opacity', runningOnCompositor: true } ]);
     });
--- a/dom/animation/test/chrome/test_animation_properties.html
+++ b/dom/animation/test/chrome/test_animation_properties.html
@@ -488,16 +488,16 @@ var gTests = [
                   values: [ value(0, '100px', 'replace', 'linear'),
                             value(1, '200px', 'replace') ] } ]
   }
 ];
 
 gTests.forEach(function(subtest) {
   test(function(t) {
     var div = addDiv(t);
-    var animation = div.animate(subtest.frames, 1000);
+    var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
     assert_properties_equal(animation.effect.getProperties(),
                             subtest.expected);
   }, subtest.desc);
 });
 
 </script>
 </body>
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -332,40 +332,40 @@ waitForAllPaints(function() {
   });
 
   add_task_if_omta_enabled(function* only_one_restyling_when_current_time_is_set_to_middle_of_duration() {
     var div = addDiv(null, { style: 'animation: opacity 100s' });
     var animation = div.getAnimations()[0];
 
     yield animation.ready;
 
-    animation.currentTime = 50000; // 50s
+    animation.currentTime = 50 * MS_PER_SEC;
 
     var markers = yield observeStyling(5);
     is(markers.length, 1,
        'Bug 1235478: Animations running on the compositor should only once ' +
        'update style when currentTime is set to middle of duration time');
     yield ensureElementRemoval(div);
   });
 
   add_task_if_omta_enabled(function* change_duration_and_currenttime() {
     var div = addDiv(null);
-    var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); //100s
+    var animation = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
     yield animation.ready;
     ok(animation.isRunningOnCompositor);
 
     // Set currentTime to a time longer than duration.
-    animation.currentTime = 500000; // 500s
+    animation.currentTime = 500 * MS_PER_SEC;
 
     // Now the animation immediately get back from compositor.
     ok(!animation.isRunningOnCompositor);
 
     // Extend the duration.
-    animation.effect.timing.duration = 800000; // 800s
+    animation.effect.timing.duration = 800 * MS_PER_SEC;
     var markers = yield observeStyling(5);
     is(markers.length, 1,
        'Animations running on the compositor should update style ' +
        'when timing.duration is made longer than the current time');
 
     yield ensureElementRemoval(div);
   });
 
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -123,17 +123,17 @@ promise_test(function(t) {
   }));
 }, 'isRunningOnCompositor is false when the animation.finish() is called');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(t.step_func(function() {
-    animation.currentTime = 100000; // 100s
+    animation.currentTime = 100 * MS_PER_SEC;
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after manually seeking the animation to the end');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
@@ -281,114 +281,119 @@ promise_test(function(t) {
        + 'property that cannot (due to Gecko limitations) but where the latter'
        + 'property is overridden in the CSS cascade, the animation should '
        + 'still report that it is running on the compositor');
   }));
 }, 'isRunningOnCompositor is true when a property that would otherwise block ' +
    'running on the compositor is overridden in the CSS cascade');
 
 promise_test(function(t) {
-  var div = addDiv(t);
-  var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); // 100s
+  var animation = addDivAndAnimate(t,
+                                   {},
+                                   { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
 
   return animation.ready.then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
-    animation.currentTime = 50000; // 50s
-    animation.effect.timing.duration = 10000; // 10s
+    animation.currentTime = 150 * MS_PER_SEC;
+    animation.effect.timing.duration = 100 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' when the animation is set a shorter duration than current time');
   }));
 }, 'animation is immediately removed from compositor' +
    'when timing.duration is made shorter than the current time');
 
 promise_test(function(t) {
-  var div = addDiv(t);
-  var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); // 100s
+  var animation = addDivAndAnimate(t,
+                                   {},
+                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   return animation.ready.then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
-    animation.currentTime = 500000; // 500s
+    animation.currentTime = 500 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when finished');
 
-    animation.effect.timing.duration = 1000000; // 1000s
+    animation.effect.timing.duration = 1000 * MS_PER_SEC;
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when restarted');
   }));
 }, 'animation is added to compositor' +
    ' when timing.duration is made longer than the current time');
 
 promise_test(function(t) {
-  var div = addDiv(t);
-  var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); // 100s
+  var animation = addDivAndAnimate(t,
+                                   {},
+                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   return animation.ready.then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
-    animation.effect.timing.endDelay = 100000; // 100s
+    animation.effect.timing.endDelay = 100 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when endDelay is changed');
 
-    animation.currentTime = 110000; // 110s
+    animation.currentTime = 110 * MS_PER_SEC;
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when currentTime is during endDelay');
   }));
 }, 'animation is removed from compositor' +
    ' when current time is made longer than the duration even during endDelay');
 
 promise_test(function(t) {
-  var div = addDiv(t);
-  var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); // 100s
+  var animation = addDivAndAnimate(t,
+                                   {},
+                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   return animation.ready.then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
-    animation.effect.timing.endDelay = -200000; // -200s
+    animation.effect.timing.endDelay = -200 * MS_PER_SEC;
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when endTime is negative value');
   }));
 }, 'animation is removed from compositor' +
    ' when endTime is negative value');
 
 promise_test(function(t) {
-  var div = addDiv(t);
-  var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); // 100s
+  var animation = addDivAndAnimate(t,
+                                   {},
+                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   return animation.ready.then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
-    animation.effect.timing.endDelay = -50000; // 50s
+    animation.effect.timing.endDelay = -50 * MS_PER_SEC;
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when endTime is positive and endDelay is negative');
-    animation.currentTime = 60000; // 60s
+    animation.currentTime = 60 * MS_PER_SEC;
     return waitForFrame();
   })).then(t.step_func(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when currentTime is after endTime');
   }));
 }, 'animation is NOT running on compositor' +
    ' when endTime is positive and endDelay is negative');
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -1,12 +1,53 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
+ * Use this variable if you specify duration or some other properties
+ * for script animation.
+ * E.g., div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
+ *
+ * NOTE: Creating animations with short duration may cause intermittent
+ * failures in asynchronous test. For example, the short duration animation
+ * might be finished when animation.ready has been fulfilled because of slow
+ * platforms or busyness of the main thread.
+ * Setting short duration to cancel its animation does not matter but
+ * if you don't want to cancel the animation, consider using longer duration.
+ */
+const MS_PER_SEC = 1000;
+
+/**
+ * Appends a div to the document body and creates an animation on the div.
+ * NOTE: This function asserts when trying to create animations with durations
+ * shorter than 100s because the shorter duration may cause intermittent
+ * failures.  If you are not sure how long it is suitable, use 100s; it's
+ * long enough but shorter than our test framework timeout (330s).
+ * If you really need to use shorter durations, use animate() function directly.
+ *
+ * @param t  The testharness.js Test object. If provided, this will be used
+ *           to register a cleanup callback to remove the div when the test
+ *           finishes.
+ * @param attrs  A dictionary object with attribute names and values to set on
+ *               the div.
+ * @param frames  The keyframes passed to Element.animate().
+ * @param options  The options passed to Element.animate().
+ */
+function addDivAndAnimate(t, attrs, frames, options) {
+  let animDur = (typeof options === 'object') ?
+    options.duration : options;
+  assert_greater_than_equal(animDur, 100 * MS_PER_SEC,
+      'Clients of this addDivAndAnimate API must request a duration ' +
+      'of at least 100s, to avoid intermittent failures from e.g.' +
+      'the main thread being busy for an extended period');
+
+  return addDiv(t, attrs).animate(frames, options);
+}
+
+/**
  * Appends a div to the document body.
  *
  * @param t  The testharness.js Test object. If provided, this will be used
  *           to register a cleanup callback to remove the div when the test
  *           finishes.
  *
  * @param attrs  A dictionary object with attribute names and values to set on
  *               the div.
--- a/dom/base/AnonymousContent.cpp
+++ b/dom/base/AnonymousContent.cpp
@@ -136,17 +136,17 @@ AnonymousContent::GetCanvasContext(const
 
   return context.forget();
 }
 
 Element*
 AnonymousContent::GetElementById(const nsAString& aElementId)
 {
   // This can be made faster in the future if needed.
-  nsCOMPtr<nsIAtom> elementId = do_GetAtom(aElementId);
+  nsCOMPtr<nsIAtom> elementId = NS_Atomize(aElementId);
   for (nsIContent* kid = mContentNode->GetFirstChild(); kid;
        kid = kid->GetNextNode(mContentNode)) {
     if (!kid->IsElement()) {
       continue;
     }
     nsIAtom* id = kid->AsElement()->GetID();
     if (id && id == elementId) {
       return kid->AsElement();
--- a/dom/base/DOMImplementation.cpp
+++ b/dom/base/DOMImplementation.cpp
@@ -65,17 +65,17 @@ DOMImplementation::CreateDocumentType(co
     return nullptr;
   }
 
   aRv = nsContentUtils::CheckQName(aQualifiedName);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aQualifiedName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aQualifiedName);
   if (!name) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
   // Indicate that there is no internal subset (not just an empty one)
   RefPtr<DocumentType> docType =
     NS_NewDOMDocumentType(mOwner->NodeInfoManager(), name, aPublicId,
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1184,20 +1184,20 @@ Element::SetAttribute(const nsAString& a
     if (aError.Failed()) {
       return;
     }
 
     nsCOMPtr<nsIAtom> nameAtom;
     if (IsHTMLElement() && IsInHTMLDocument()) {
       nsAutoString lower;
       nsContentUtils::ASCIIToLower(aName, lower);
-      nameAtom = do_GetAtom(lower);
+      nameAtom = NS_Atomize(lower);
     }
     else {
-      nameAtom = do_GetAtom(aName);
+      nameAtom = NS_Atomize(aName);
     }
     if (!nameAtom) {
       aError.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, true);
     return;
   }
@@ -1269,17 +1269,17 @@ Element::GetAttributeNS(const nsAString&
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
 
   if (nsid == kNameSpaceID_Unknown) {
     // Unknown namespace means no attribute.
     SetDOMStringToNull(aReturn);
     return;
   }
 
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
   bool hasAttr = GetAttr(nsid, name, aReturn);
   if (!hasAttr) {
     SetDOMStringToNull(aReturn);
   }
 }
 
 void
 Element::SetAttributeNS(const nsAString& aNamespaceURI,
@@ -1301,17 +1301,17 @@ Element::SetAttributeNS(const nsAString&
                    aValue, true);
 }
 
 void
 Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            ErrorResult& aError)
 {
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
   int32_t nsid =
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
 
   if (nsid == kNameSpaceID_Unknown) {
     // If the namespace ID is unknown, it means there can't possibly be an
     // existing attribute. We would need a known namespace ID to pass into
     // UnsetAttr, so we return early if we don't have one.
     return;
@@ -1387,17 +1387,17 @@ Element::HasAttributeNS(const nsAString&
   int32_t nsid =
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
 
   if (nsid == kNameSpaceID_Unknown) {
     // Unknown namespace means no attr...
     return false;
   }
 
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
   return HasAttr(nsid, name);
 }
 
 already_AddRefed<nsIHTMLCollection>
 Element::GetElementsByClassName(const nsAString& aClassNames)
 {
   return nsContentUtils::GetElementsByClassName(this, aClassNames);
 }
@@ -2095,17 +2095,17 @@ Element::DispatchClickEvent(nsPresContex
     pressure = sourceMouseEvent->pressure;
     inputSource = sourceMouseEvent->inputSource;
   } else if (aSourceEvent->mClass == eKeyboardEventClass) {
     inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
   }
   event.pressure = pressure;
   event.clickCount = clickCount;
   event.inputSource = inputSource;
-  event.modifiers = aSourceEvent->modifiers;
+  event.mModifiers = aSourceEvent->mModifiers;
   if (aExtraEventFlags) {
     // Be careful not to overwrite existing flags!
     event.mFlags.Union(*aExtraEventFlags);
   }
 
   return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
 }
 
@@ -2441,17 +2441,17 @@ Element::SetAttrAndNotify(int32_t aNames
     Attr* attrNode =
       GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
     mutation.mRelatedNode = attrNode;
 
     mutation.mAttrName = aName;
     nsAutoString newValue;
     GetAttr(aNamespaceID, aName, newValue);
     if (!newValue.IsEmpty()) {
-      mutation.mNewAttrValue = do_GetAtom(newValue);
+      mutation.mNewAttrValue = NS_Atomize(newValue);
     }
     if (!oldValue->IsEmptyString()) {
       mutation.mPrevAttrValue = oldValue->GetAsAtom();
     }
     mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(OwnerDoc(), this);
     (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
@@ -2679,17 +2679,17 @@ Element::UnsetAttr(int32_t aNameSpaceID,
     InternalMutationEvent mutation(true, eLegacyAttrModified);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
     oldValue.ToString(value);
     if (!value.IsEmpty())
-      mutation.mPrevAttrValue = do_GetAtom(value);
+      mutation.mPrevAttrValue = NS_Atomize(value);
     mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
     mozAutoSubtreeModified subtree(OwnerDoc(), this);
     (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -275,17 +275,17 @@ nsIContent::LookupNamespaceURIInternal(c
   if (aNamespacePrefix.EqualsLiteral("xmlns")) {
     // Special-case for xmlns prefix
     aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
     return NS_OK;
   }
 
   nsCOMPtr<nsIAtom> name;
   if (!aNamespacePrefix.IsEmpty()) {
-    name = do_GetAtom(aNamespacePrefix);
+    name = NS_Atomize(aNamespacePrefix);
     NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
   }
   else {
     name = nsGkAtoms::xmlns;
   }
   // Trace up the content parent chain looking for the namespace
   // declaration that declares aNamespacePrefix.
   const nsIContent* content = this;
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -823,17 +823,17 @@ TextInputProcessor::KeydownInternal(cons
       ActivateModifierKey(modifierKeyData);
     }
     if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
       return NS_OK;
     }
   } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
     return NS_ERROR_INVALID_ARG;
   }
-  keyEvent.modifiers = GetActiveModifiers();
+  keyEvent.mModifiers = GetActiveModifiers();
 
   RefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
@@ -903,17 +903,17 @@ TextInputProcessor::KeyupInternal(const 
       InactivateModifierKey(ModifierKeyData(keyEvent));
     }
     if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
       return NS_OK;
     }
   } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
     return NS_ERROR_INVALID_ARG;
   }
-  keyEvent.modifiers = GetActiveModifiers();
+  keyEvent.mModifiers = GetActiveModifiers();
 
   RefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -730,29 +730,29 @@ nsAttrValue::ToString(nsAString& aResult
   }
 }
 
 already_AddRefed<nsIAtom>
 nsAttrValue::GetAsAtom() const
 {
   switch (Type()) {
     case eString:
-      return do_GetAtom(GetStringValue());
+      return NS_Atomize(GetStringValue());
 
     case eAtom:
       {
         nsCOMPtr<nsIAtom> atom = GetAtomValue();
         return atom.forget();
       }
 
     default:
       {
         nsAutoString val;
         ToString(val);
-        return do_GetAtom(val);
+        return NS_Atomize(val);
       }
   }
 }
 
 const nsCheapString
 nsAttrValue::GetStringValue() const
 {
   NS_PRECONDITION(Type() == eString, "wrong type");
@@ -1223,17 +1223,17 @@ nsAttrValue::Contains(const nsAString& a
   return false;
 }
 
 void
 nsAttrValue::ParseAtom(const nsAString& aValue)
 {
   ResetIfSet();
 
-  nsCOMPtr<nsIAtom> atom = NS_NewAtom(aValue);
+  nsCOMPtr<nsIAtom> atom = NS_Atomize(aValue);
   if (atom) {
     SetPtrValueAndType(atom.forget().take(), eAtomBase);
   }
 }
 
 void
 nsAttrValue::ParseAtomArray(const nsAString& aValue)
 {
@@ -1255,17 +1255,17 @@ nsAttrValue::ParseAtomArray(const nsAStr
 
   nsAString::const_iterator start(iter);
 
   // get first - and often only - atom
   do {
     ++iter;
   } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
 
-  nsCOMPtr<nsIAtom> classAtom = do_GetAtom(Substring(start, iter));
+  nsCOMPtr<nsIAtom> classAtom = NS_Atomize(Substring(start, iter));
   if (!classAtom) {
     Reset();
     return;
   }
 
   // skip whitespace
   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
     hasSpace = true;
@@ -1296,17 +1296,17 @@ nsAttrValue::ParseAtomArray(const nsAStr
   // parse the rest of the classnames
   while (iter != end) {
     start = iter;
 
     do {
       ++iter;
     } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
 
-    classAtom = do_GetAtom(Substring(start, iter));
+    classAtom = NS_Atomize(Substring(start, iter));
 
     if (!array->AppendElement(classAtom)) {
       Reset();
       return;
     }
 
     // skip whitespace
     while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
@@ -1713,17 +1713,17 @@ nsAttrValue::SetMiscAtomOrString(const n
     // * We're allowing enumerated values because sometimes the empty
     //   string corresponds to a particular enumerated value, especially
     //   for enumerated values that are not limited enumerated.
     // Add other types as needed.
     NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum,
                  "Empty string?");
     MiscContainer* cont = GetMiscContainer();
     if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
-      nsCOMPtr<nsIAtom> atom = NS_NewAtom(*aValue);
+      nsCOMPtr<nsIAtom> atom = NS_Atomize(*aValue);
       if (atom) {
         cont->mStringBits =
           reinterpret_cast<uintptr_t>(atom.forget().take()) | eAtomBase;
       }
     } else {
       nsStringBuffer* buf = GetStringBuffer(*aValue).take();
       if (buf) {
         cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase;
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -223,22 +223,22 @@ NS_GetContentList(nsINode* aRootNode,
   auto entry = static_cast<ContentListHashEntry*>
                           (gContentListHashTable->Add(&hashKey, fallible));
   if (entry)
     list = entry->mContentList;
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
-    nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
+    nsCOMPtr<nsIAtom> xmlAtom = NS_Atomize(aTagname);
     nsCOMPtr<nsIAtom> htmlAtom;
     if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
       nsAutoString lowercaseName;
       nsContentUtils::ASCIIToLower(aTagname, lowercaseName);
-      htmlAtom = do_GetAtom(lowercaseName);
+      htmlAtom = NS_Atomize(lowercaseName);
     } else {
       htmlAtom = xmlAtom;
     }
     list = new nsContentList(aRootNode, aMatchNameSpaceId, htmlAtom, xmlAtom);
     if (entry) {
       entry->mContentList = list;
     }
   }
@@ -512,17 +512,17 @@ nsContentList::NamedItem(const nsAString
     return nullptr;
   }
 
   BringSelfUpToDate(aDoFlush);
 
   uint32_t i, count = mElements.Length();
 
   // Typically IDs and names are atomized
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
   NS_ENSURE_TRUE(name, nullptr);
 
   for (i = 0; i < count; i++) {
     nsIContent *content = mElements[i];
     // XXX Should this pass eIgnoreCase?
     if (content &&
         ((content->IsHTMLElement() &&
           content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -801,17 +801,17 @@ nsContentSink::ProcessMETATag(nsIContent
     if (nsGkAtoms::refresh->Equals(header) &&
         (mDocument->GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES)) {
       return NS_OK;
     }
 
     nsAutoString result;
     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
     if (!result.IsEmpty()) {
-      nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header));
+      nsCOMPtr<nsIAtom> fieldAtom(NS_Atomize(header));
       rv = ProcessHeaderData(fieldAtom, result, aContent); 
     }
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
                             nsGkAtoms::handheldFriendly, eIgnoreCase)) {
     nsAutoString result;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1523,33 +1523,42 @@ nsContentUtils::CopyNewlineNormalizedUni
   aDest.BeginWriting(iter);
   sink_traits dest_traits(iter);
   CopyNormalizeNewlines<sink_traits> normalizer(&dest_traits);
   copy_string(aSrcStart, aSrcEnd, normalizer);
   return normalizer.GetCharsWritten();
 }
 
 /**
- * This is used to determine whether a character is in one of the punctuation
- * mark classes which CSS says should be part of the first-letter.
- * See http://www.w3.org/TR/CSS2/selector.html#first-letter and
- *     http://www.w3.org/TR/selectors/#first-letter
+ * This is used to determine whether a character is in one of the classes
+ * which CSS says should be part of the first-letter.  Currently, that is
+ * all punctuation classes (P*).  Note that this is a change from CSS2
+ * which excluded Pc and Pd.
+ *
+ * https://www.w3.org/TR/css-pseudo-4/#first-letter-pseudo
+ * "Punctuation (i.e, characters that belong to the Punctuation (P*) Unicode
+ *  general category [UAX44]) [...]"
  */
 
 // static
 bool
 nsContentUtils::IsFirstLetterPunctuation(uint32_t aChar)
 {
-  uint8_t cat = mozilla::unicode::GetGeneralCategory(aChar);
-
-  return (cat == HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION ||     // Ps
-          cat == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION ||    // Pe
-          cat == HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION ||  // Pi
-          cat == HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION ||    // Pf
-          cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION);     // Po
+  switch (mozilla::unicode::GetGeneralCategory(aChar)) {
+    case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
+    case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION:    /* Pd */
+    case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION:   /* Pe */
+    case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION:   /* Pf */
+    case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
+    case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION:   /* Po */
+    case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION:    /* Ps */
+      return true;
+    default:
+      return false;
+  }
 }
 
 // static
 bool
 nsContentUtils::IsFirstLetterPunctuationAt(const nsTextFragment* aFrag, uint32_t aOffset)
 {
   char16_t h = aFrag->CharAt(aOffset);
   if (!IS_SURROGATE(h)) {
@@ -2845,21 +2854,21 @@ nsContentUtils::SplitQName(const nsICont
                                                                   colon),
                                                         nameSpace);
     NS_ENSURE_SUCCESS(rv, rv);
 
     *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace);
     if (*aNamespace == kNameSpaceID_Unknown)
       return NS_ERROR_FAILURE;
 
-    *aLocalName = NS_NewAtom(Substring(colon + 1, end)).take();
+    *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
   }
   else {
     *aNamespace = kNameSpaceID_None;
-    *aLocalName = NS_NewAtom(aQName).take();
+    *aLocalName = NS_Atomize(aQName).take();
   }
   NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
   return NS_OK;
 }
 
 // static
 nsresult
 nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI,
@@ -2874,17 +2883,17 @@ nsContentUtils::GetNodeInfoFromQName(con
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t nsID;
   sNameSpaceManager->RegisterNameSpace(aNamespaceURI, nsID);
   if (colon) {
     const char16_t* end;
     qName.EndReading(end);
 
-    nsCOMPtr<nsIAtom> prefix = do_GetAtom(Substring(qName.get(), colon));
+    nsCOMPtr<nsIAtom> prefix = NS_Atomize(Substring(qName.get(), colon));
 
     rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix,
                                        nsID, aNodeType, aNodeInfo);
   }
   else {
     rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID,
                                        aNodeType, aNodeInfo);
   }
@@ -2934,30 +2943,30 @@ nsContentUtils::SplitExpatName(const cha
     }
     else {
       *aNameSpaceID = kNameSpaceID_Unknown;
     }
 
     nameStart = (uriEnd + 1);
     if (nameEnd)  {
       const char16_t *prefixStart = nameEnd + 1;
-      *aPrefix = NS_NewAtom(Substring(prefixStart, pos)).take();
+      *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take();
     }
     else {
       nameEnd = pos;
       *aPrefix = nullptr;
     }
   }
   else {
     *aNameSpaceID = kNameSpaceID_None;
     nameStart = aExpatName;
     nameEnd = pos;
     *aPrefix = nullptr;
   }
-  *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd)).take();
+  *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take();
 }
 
 // static
 nsPresContext*
 nsContentUtils::GetContextForContent(const nsIContent* aContent)
 {
   nsIDocument* doc = aContent->GetComposedDoc();
   if (doc) {
@@ -3705,17 +3714,17 @@ nsContentUtils::GetEventMessageAndAtom(c
     while (sUserDefinedEvents->Count() > 64) {
       nsIAtom* first = sUserDefinedEvents->ObjectAt(0);
       sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
       sUserDefinedEvents->RemoveObjectAt(0);
     }
   }
 
   *aEventMessage = eUnidentifiedEvent;
-  nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aName);
+  nsCOMPtr<nsIAtom> atom = NS_Atomize(NS_LITERAL_STRING("on") + aName);
   sUserDefinedEvents->AppendObject(atom);
   mapping.mAtom = atom;
   mapping.mMessage = eUnidentifiedEvent;
   mapping.mType = EventNameType_None;
   mapping.mEventClassID = eBasicEventClass;
   sStringEventTable->Put(aName, mapping);
   return mapping.mAtom;
 }
@@ -3848,17 +3857,17 @@ nsContentUtils::MatchElementId(nsIConten
 
 /* static */
 Element *
 nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
 {
   NS_PRECONDITION(!aId.IsEmpty(), "Will match random elements");
   
   // ID attrs are generally stored as atoms, so just atomize this up front
-  nsCOMPtr<nsIAtom> id(do_GetAtom(aId));
+  nsCOMPtr<nsIAtom> id(NS_Atomize(aId));
   if (!id) {
     // OOM, so just bail
     return nullptr;
   }
 
   return MatchElementId(aContent, id);
 }
 
@@ -5121,17 +5130,17 @@ static void ProcessViewportToken(nsIDocu
     nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(tail, tip),
                                                         true);
   const nsAString &value =
     nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(Substring(++tip, end),
                                                         true);
 
   /* Check for known keys. If we find a match, insert the appropriate
    * information into the document header. */
-  nsCOMPtr<nsIAtom> key_atom = do_GetAtom(key);
+  nsCOMPtr<nsIAtom> key_atom = NS_Atomize(key);
   if (key_atom == nsGkAtoms::height)
     aDocument->SetHeaderData(nsGkAtoms::viewport_height, value);
   else if (key_atom == nsGkAtoms::width)
     aDocument->SetHeaderData(nsGkAtoms::viewport_width, value);
   else if (key_atom == nsGkAtoms::initial_scale)
     aDocument->SetHeaderData(nsGkAtoms::viewport_initial_scale, value);
   else if (key_atom == nsGkAtoms::minimum_scale)
     aDocument->SetHeaderData(nsGkAtoms::viewport_minimum_scale, value);
@@ -5224,17 +5233,17 @@ nsContentUtils::GetDragSession()
     dragService->GetCurrentSession(getter_AddRefs(dragSession));
   return dragSession.forget();
 }
 
 /* static */
 nsresult
 nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent)
 {
-  if (aDragEvent->dataTransfer || !aDragEvent->IsTrusted()) {
+  if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
     return NS_OK;
   }
 
   // For draggesture and dragstart events, the data transfer object is
   // created before the event fires, so it should already be set. For other
   // drag events, get the object from the drag session.
   NS_ASSERTION(aDragEvent->mMessage != eLegacyDragGesture &&
                aDragEvent->mMessage != eDragStart,
@@ -5266,40 +5275,43 @@ nsContentUtils::SetDataTransferInEvent(W
   bool isCrossDomainSubFrameDrop = false;
   if (aDragEvent->mMessage == eDrop ||
       aDragEvent->mMessage == eLegacyDragDrop) {
     isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
   }
 
   // each event should use a clone of the original dataTransfer.
   initialDataTransfer->Clone(aDragEvent->target, aDragEvent->mMessage,
-                             aDragEvent->userCancelled,
+                             aDragEvent->mUserCancelled,
                              isCrossDomainSubFrameDrop,
-                             getter_AddRefs(aDragEvent->dataTransfer));
-  NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
+                             getter_AddRefs(aDragEvent->mDataTransfer));
+  if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   // for the dragenter and dragover events, initialize the drop effect
   // from the drop action, which platform specific widget code sets before
   // the event is fired based on the keyboard state.
   if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
     uint32_t action, effectAllowed;
     dragSession->GetDragAction(&action);
-    aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed);
-    aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
+    aDragEvent->mDataTransfer->GetEffectAllowedInt(&effectAllowed);
+    aDragEvent->mDataTransfer->SetDropEffectInt(
+                                 FilterDropEffect(action, effectAllowed));
   }
   else if (aDragEvent->mMessage == eDrop ||
            aDragEvent->mMessage == eLegacyDragDrop ||
            aDragEvent->mMessage == eDragEnd) {
     // For the drop and dragend events, set the drop effect based on the
     // last value that the dropEffect had. This will have been set in
     // EventStateManager::PostHandleEvent for the last dragenter or
     // dragover event.
     uint32_t dropEffect;
     initialDataTransfer->GetDropEffectInt(&dropEffect);
-    aDragEvent->dataTransfer->SetDropEffectInt(dropEffect);
+    aDragEvent->mDataTransfer->SetDropEffectInt(dropEffect);
   }
 
   return NS_OK;
 }
 
 /* static */
 uint32_t
 nsContentUtils::FilterDropEffect(uint32_t aAction, uint32_t aEffectAllowed)
@@ -7632,17 +7644,17 @@ nsContentUtils::SendKeyEvent(nsIWidget* 
   else if (aType.EqualsLiteral("keyup"))
     msg = eKeyUp;
   else if (aType.EqualsLiteral("keypress"))
     msg = eKeyPress;
   else
     return NS_ERROR_FAILURE;
 
   WidgetKeyboardEvent event(true, msg, aWidget);
-  event.modifiers = GetWidgetModifiers(aModifiers);
+  event.mModifiers = GetWidgetModifiers(aModifiers);
 
   if (msg == eKeyPress) {
     event.keyCode = aCharCode ? 0 : aKeyCode;
     event.charCode = aCharCode;
   } else {
     event.keyCode = aKeyCode;
     event.charCode = 0;
   }
@@ -7764,17 +7776,17 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<
 
   if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
     aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   }
 
   WidgetMouseEvent event(true, msg, widget, WidgetMouseEvent::eReal,
                          contextMenuKey ? WidgetMouseEvent::eContextMenuKey :
                                           WidgetMouseEvent::eNormal);
-  event.modifiers = GetWidgetModifiers(aModifiers);
+  event.mModifiers = GetWidgetModifiers(aModifiers);
   event.button = aButton;
   event.buttons = GetButtonsFlagForButton(aButton);
   event.widget = widget;
   event.pressure = aPressure;
   event.inputSource = aInputSourceArg;
   event.clickCount = aClickCount;
   event.mTime = PR_IntervalNow();
   event.mFlags.mIsSynthesizedForTests = aIsSynthesized;
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -678,17 +678,17 @@ nsDOMMutationObserver::Observe(nsINode& 
   if (aOptions.mAttributeFilter.WasPassed()) {
     allAttrs = false;
     const mozilla::dom::Sequence<nsString>& filtersAsString =
       aOptions.mAttributeFilter.Value();
     uint32_t len = filtersAsString.Length();
     filters.SetCapacity(len);
 
     for (uint32_t i = 0; i < len; ++i) {
-      nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]);
+      nsCOMPtr<nsIAtom> a = NS_Atomize(filtersAsString[i]);
       filters.AppendObject(a);
     }
   }
 
   nsMutationReceiver* r = GetReceiverFor(&aTarget, true, animations);
   r->SetChildList(childList);
   r->SetAttributes(attributes);
   r->SetCharacterData(characterData);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -234,17 +234,17 @@ nsDOMWindowUtils::GetDocCharsetIsForced(
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName,
                                       nsAString& aValue)
 {
   nsIDocument* doc = GetDocument();
   if (doc) {
-    nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
+    nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
     doc->GetHeaderData(name, aValue);
     return NS_OK;
   }
 
   aValue.Truncate();
   return NS_OK;
 }
 
@@ -664,17 +664,17 @@ nsDOMWindowUtils::SendPointerEventCommon
     return NS_ERROR_FAILURE;
   }
 
   if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
     aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   }
 
   WidgetPointerEvent event(true, msg, widget);
-  event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
+  event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.button = aButton;
   event.buttons = nsContentUtils::GetButtonsFlagForButton(aButton);
   event.widget = widget;
   event.pressure = aPressure;
   event.inputSource = aInputSourceArg;
   event.pointerId = aPointerId;
   event.width = aWidth;
   event.height = aHeight;
@@ -787,17 +787,17 @@ nsDOMWindowUtils::SendWheelEvent(float a
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
   if (!widget) {
     return NS_ERROR_NULL_POINTER;
   }
 
   WidgetWheelEvent wheelEvent(true, eWheel, widget);
-  wheelEvent.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
+  wheelEvent.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   wheelEvent.deltaX = aDeltaX;
   wheelEvent.deltaY = aDeltaY;
   wheelEvent.deltaZ = aDeltaZ;
   wheelEvent.deltaMode = aDeltaMode;
   wheelEvent.isMomentum =
     (aOptions & WHEEL_EVENT_CAUSED_BY_MOMENTUM) != 0;
   wheelEvent.mIsNoLineOrPageDelta =
     (aOptions & WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE) != 0;
@@ -932,17 +932,17 @@ nsDOMWindowUtils::SendTouchEventCommon(c
   } else if (aType.EqualsLiteral("touchend")) {
     msg = eTouchEnd;
   } else if (aType.EqualsLiteral("touchcancel")) {
     msg = eTouchCancel;
   } else {
     return NS_ERROR_UNEXPECTED;
   }
   WidgetTouchEvent event(true, msg, widget);
-  event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
+  event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.widget = widget;
   event.mTime = PR_Now();
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return NS_ERROR_FAILURE;
   }
   event.touches.SetCapacity(aCount);
@@ -1287,17 +1287,17 @@ nsDOMWindowUtils::SendSimpleGestureEvent
     msg = eEdgeUICanceled;
   } else if (aType.EqualsLiteral("MozEdgeUICompleted")) {
     msg = eEdgeUICompleted;
   } else {
     return NS_ERROR_FAILURE;
   }
 
   WidgetSimpleGestureEvent event(true, msg, widget);
-  event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
+  event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.direction = aDirection;
   event.delta = aDelta;
   event.clickCount = aClickCount;
   event.mTime = PR_IntervalNow();
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext)
     return NS_ERROR_FAILURE;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5507,17 +5507,17 @@ nsDocument::SetupCustomElement(Element* 
                                const nsAString* aTypeExtension)
 {
   if (!mRegistry) {
     return;
   }
 
   nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
   nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
-    do_GetAtom(*aTypeExtension) : tagAtom;
+    NS_Atomize(*aTypeExtension) : tagAtom;
 
   if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
     // Custom element setup in the parser happens after the "is"
     // attribute is added.
     aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
   }
 
   CustomElementDefinition* data;
@@ -5834,17 +5834,17 @@ nsDocument::CustomElementConstructor(JSC
   // Function name is the type of the custom element.
   JSString* jsFunName =
     JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
   nsAutoJSString elemName;
   if (!elemName.init(aCx, jsFunName)) {
     return true;
   }
 
-  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
+  nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
   CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
   CustomElementDefinition* definition;
   if (!document->mRegistry ||
       !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
     return true;
   }
 
   nsDependentAtomString localName(definition->mLocalName);
@@ -6128,17 +6128,17 @@ nsDocument::RegisterElement(JSContext* a
   // options.extends.
   nsAutoString lcName;
   if (IsHTMLDocument()) {
     nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
   } else {
     lcName.Assign(aOptions.mExtends);
   }
 
-  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
+  nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(lcType));
   if (!nsContentUtils::IsCustomElementName(typeAtom)) {
     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   // If there already exists a definition with the same TYPE, set ERROR to
   // DuplicateDefinition and stop.
   // Note that we need to find existing custom elements from either namespace.
@@ -6249,17 +6249,17 @@ nsDocument::RegisterElement(JSContext* a
         }
       }
     } // Done with the checks, leave prototype's compartment.
 
     // If name was provided and not null...
     if (!lcName.IsEmpty()) {
       // Let BASE be the element interface for NAME and NAMESPACE.
       bool known = false;
-      nameAtom = do_GetAtom(lcName);
+      nameAtom = NS_Atomize(lcName);
       if (namespaceID == kNameSpaceID_XHTML) {
         nsIParserService* ps = nsContentUtils::GetParserService();
         if (!ps) {
           rv.Throw(NS_ERROR_UNEXPECTED);
           return;
         }
 
         known =
@@ -6866,17 +6866,17 @@ nsDocument::GetAnonymousElementByAttribu
   return NS_OK;
 }
 
 Element*
 nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
                                             const nsAString& aAttrName,
                                             const nsAString& aAttrValue)
 {
-  nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
+  nsCOMPtr<nsIAtom> attribute = NS_Atomize(aAttrName);
 
   return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
 }
 
 
 NS_IMETHODIMP
 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
                               nsIDOMNodeList** aResult)
@@ -8615,17 +8615,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
     };
 
     nsAutoCString headerVal;
     const char *const *name = headers;
     while (*name) {
       rv =
         httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
       if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
-        nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
+        nsCOMPtr<nsIAtom> key = NS_Atomize(*name);
         SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
       }
       ++name;
     }
   } else {
     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
     if (fileChannel) {
       nsCOMPtr<nsIFile> file;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -894,16 +894,39 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   }
 
   nsIPresShell* ourShell = ourDoc->GetShell();
   nsIPresShell* otherShell = otherDoc->GetShell();
   if (!ourShell || !otherShell) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
+  if (mRemoteBrowser->IsIsolatedMozBrowserElement() !=
+      aOther->mRemoteBrowser->IsIsolatedMozBrowserElement() ||
+      mRemoteBrowser->HasOwnApp() != aOther->mRemoteBrowser->HasOwnApp()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  if (mRemoteBrowser->OriginAttributesRef() !=
+      aOther->mRemoteBrowser->OriginAttributesRef()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  bool ourHasHistory =
+    mIsTopLevelContent &&
+    ourContent->IsXULElement(nsGkAtoms::browser) &&
+    !ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
+  bool otherHasHistory =
+    aOther->mIsTopLevelContent &&
+    otherContent->IsXULElement(nsGkAtoms::browser) &&
+    !otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
+  if (ourHasHistory != otherHasHistory) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
   if (mInSwap || aOther->mInSwap) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   mInSwap = aOther->mInSwap = true;
 
   nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
   nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
   if (!ourFrame || !otherFrame) {
@@ -1046,33 +1069,72 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
                                    RefPtr<nsFrameLoader>& aFirstToSwap,
                                    RefPtr<nsFrameLoader>& aSecondToSwap)
 {
   NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
                   (aFirstToSwap == aOther && aSecondToSwap == this),
                   "Swapping some sort of random loaders?");
   NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
 
-  if (IsRemoteFrame() && aOther->IsRemoteFrame()) {
-    return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
-  }
-
-  if (IsRemoteFrame() || aOther->IsRemoteFrame()) {
+  if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
     NS_WARNING("Swapping remote and non-remote frames is not currently supported");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   Element* ourContent = mOwnerContent;
   Element* otherContent = aOther->mOwnerContent;
 
   if (!ourContent || !otherContent) {
     // Can't handle this
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
+  bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
+                      ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
+  bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
+                        otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
+  if (ourHasSrcdoc || otherHasSrcdoc) {
+    // Ignore this case entirely for now, since we support XUL <-> HTML swapping
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  bool ourPassPointerEvents =
+    ourContent->AttrValueIs(kNameSpaceID_None,
+                            nsGkAtoms::mozpasspointerevents,
+                            nsGkAtoms::_true,
+                            eCaseMatters);
+  bool otherPassPointerEvents =
+    otherContent->AttrValueIs(kNameSpaceID_None,
+                              nsGkAtoms::mozpasspointerevents,
+                              nsGkAtoms::_true,
+                              eCaseMatters);
+  if (ourPassPointerEvents != otherPassPointerEvents) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  bool ourFullscreenAllowed =
+    ourContent->IsXULElement() ||
+    (OwnerIsMozBrowserOrAppFrame() &&
+      (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
+       ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
+  bool otherFullscreenAllowed =
+    otherContent->IsXULElement() ||
+    (aOther->OwnerIsMozBrowserOrAppFrame() &&
+      (otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
+       otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
+  if (ourFullscreenAllowed != otherFullscreenAllowed) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // Divert to a separate path for the remaining steps in the remote case
+  if (IsRemoteFrame()) {
+    MOZ_ASSERT(aOther->IsRemoteFrame());
+    return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
+  }
+
   // Make sure there are no same-origin issues
   bool equal;
   nsresult rv =
     ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
   if (NS_FAILED(rv) || !equal) {
     // Security problems loom.  Just bail on it all
     return NS_ERROR_DOM_SECURITY_ERR;
   }
@@ -1198,17 +1260,22 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   nsIPresShell* otherShell = otherDoc->GetShell();
   if (!ourShell || !otherShell) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   if (ourDocshell->GetIsIsolatedMozBrowserElement() !=
       otherDocshell->GetIsIsolatedMozBrowserElement() ||
       ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) {
-      return NS_ERROR_NOT_IMPLEMENTED;
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  if (ourDocshell->GetOriginAttributes() !=
+      otherDocshell->GetOriginAttributes()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   if (mInSwap || aOther->mInSwap) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
                                      ourEventTarget, otherEventTarget);
 
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -390,17 +390,17 @@ nsGenericDOMDataNode::SetTextInternal(ui
 
     if (haveMutationListeners) {
       InternalMutationEvent mutation(true, eLegacyCharacterDataModified);
 
       mutation.mPrevAttrValue = oldValue;
       if (aLength > 0) {
         nsAutoString val;
         mText.AppendTo(val);
-        mutation.mNewAttrValue = do_GetAtom(val);
+        mutation.mNewAttrValue = NS_Atomize(val);
       }
 
       mozAutoSubtreeModified subtree(OwnerDoc(), this);
       (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
@@ -1095,17 +1095,17 @@ nsGenericDOMDataNode::AppendTextTo(nsASt
   return mText.AppendTo(aResult, aFallible);
 }
 
 already_AddRefed<nsIAtom>
 nsGenericDOMDataNode::GetCurrentValueAtom()
 {
   nsAutoString val;
   GetData(val);
-  return NS_NewAtom(val);
+  return NS_Atomize(val);
 }
 
 NS_IMETHODIMP
 nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
 {
   return NS_OK;
 }
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -934,16 +934,17 @@ GK_ATOM(ontransitionend, "ontransitionen
 GK_ATOM(onunderflow, "onunderflow")
 GK_ATOM(onunload, "onunload")
 GK_ATOM(onupdatefound, "onupdatefound")
 GK_ATOM(onupdateready, "onupdateready")
 GK_ATOM(onupgradeneeded, "onupgradeneeded")
 GK_ATOM(onussdreceived, "onussdreceived")
 GK_ATOM(onversionchange, "onversionchange")
 GK_ATOM(onvoicechange, "onvoicechange")
+GK_ATOM(onvoiceschanged, "onvoiceschanged")
 GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd")
 GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration")
 GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart")
 GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd")
 GK_ATOM(onwheel, "onwheel")
 GK_ATOM(open, "open")
 GK_ATOM(optgroup, "optgroup")
 GK_ATOM(optimum, "optimum")
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -230,21 +230,9 @@ interface nsIFrameLoaderOwner : nsISuppo
    * iframes.
    */
   readonly attribute mozIApplication parentApplication;
 
   /**
    * Puts the FrameLoaderOwner in prerendering mode.
    */
   void setIsPrerendered();
-
-  /**
-   * Swap frame loaders with the given nsIFrameLoaderOwner.  This may
-   * only be posible in a very limited range of circumstances, or
-   * never, depending on the object implementing this interface.
-   *
-   * @throws NS_ERROR_NOT_IMPLEMENTED if the swapping logic is not
-   *   implemented for the two given frame loader owners.
-   * @throws NS_ERROR_DOM_SECURITY_ERR if the swap is not allowed on
-   *   security grounds.
-   */
-  void swapFrameLoaders(in nsIFrameLoaderOwner aOtherOwner);
 };
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -775,17 +775,17 @@ SetUserDataProperty(uint16_t aCategory, 
 }
 
 nsresult
 nsINode::SetUserData(const nsAString &aKey, nsIVariant *aData, nsIVariant **aResult)
 {
   OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData);
   *aResult = nullptr;
 
-  nsCOMPtr<nsIAtom> key = do_GetAtom(aKey);
+  nsCOMPtr<nsIAtom> key = NS_Atomize(aKey);
   if (!key) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv;
   void *data;
   if (aData) {
     rv = SetUserDataProperty(DOM_USER_DATA, this, key, aData, &data);
@@ -828,17 +828,17 @@ nsINode::SetUserData(JSContext* aCx, con
   aError = nsContentUtils::XPConnect()->VariantToJS(aCx, GetWrapper(), oldData,
                                                     aRetval);
 }
 
 nsIVariant*
 nsINode::GetUserData(const nsAString& aKey)
 {
   OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData);
-  nsCOMPtr<nsIAtom> key = do_GetAtom(aKey);
+  nsCOMPtr<nsIAtom> key = NS_Atomize(aKey);
   if (!key) {
     return nullptr;
   }
 
   return static_cast<nsIVariant*>(GetProperty(DOM_USER_DATA, key));
 }
 
 void
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2459,16 +2459,24 @@ SetMemoryGCDynamicHeapGrowthPrefChangedC
 static void
 SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
   JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
 }
 
 static void
+SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  bool pref = Preferences::GetBool(aPrefName);
+  JS_SetGCParameter(sRuntime, JSGC_REFRESH_FRAME_SLICES_ENABLED, pref);
+}
+
+
+static void
 SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
   sIncrementalCC = pref;
 }
 
 static bool
 AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
@@ -2558,16 +2566,19 @@ nsJSContext::EnsureStatics()
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_high_frequency_time_limit_ms",
                                        (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
                                        "javascript.options.mem.gc_dynamic_mark_slice");
 
+  Preferences::RegisterCallbackAndCall(SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback,
+                                       "javascript.options.mem.gc_refresh_frame_slices_enabled");
+
   Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
                                        "javascript.options.mem.gc_dynamic_heap_growth");
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_low_frequency_heap_growth",
                                        (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -260,34 +260,34 @@ nsNodeInfoManager::GetNodeInfo(nsIAtom *
 
 nsresult
 nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
                                int32_t aNamespaceID, uint16_t aNodeType,
                                NodeInfo** aNodeInfo)
 {
 #ifdef DEBUG
   {
-    nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
+    nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
     CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr);
   }
 #endif
 
   NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
 
   void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
 
   if (node) {
     NodeInfo* nodeInfo = static_cast<NodeInfo *>(node);
 
     NS_ADDREF(*aNodeInfo = nodeInfo);
 
     return NS_OK;
   }
 
-  nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
+  nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
   NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
 
   RefPtr<NodeInfo> newNodeInfo =
     new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this);
   NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   PLHashEntry *he;
   he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1244,22 +1244,16 @@ nsObjectLoadingContent::GetParentApplica
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SetIsPrerendered()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 nsObjectLoadingContent::GetActualType(nsACString& aType)
 {
   aType = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetDisplayedType(uint32_t* aType)
@@ -1661,17 +1655,17 @@ nsObjectLoadingContent::CheckProcessPoli
     return false;
   }
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "Must be an instance of content");
 
   nsIDocument* doc = thisContent->OwnerDoc();
-  
+
   int32_t objectType;
   switch (mType) {
     case eType_Image:
       objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
       break;
     case eType_Document:
       objectType = nsIContentPolicy::TYPE_DOCUMENT;
       break;
@@ -2928,17 +2922,17 @@ nsObjectLoadingContent::PluginCrashed(ns
 
   PluginDestroyed();
 
   // Switch to fallback/crashed state, notify
   LoadFallback(eFallbackCrashed, true);
 
   // send nsPluginCrashedEvent
 
-  // Note that aPluginTag in invalidated after we're called, so copy 
+  // Note that aPluginTag in invalidated after we're called, so copy
   // out any data we need now.
   nsAutoCString pluginName;
   aPluginTag->GetName(pluginName);
   nsAutoCString pluginFilename;
   aPluginTag->GetFilename(pluginFilename);
 
   nsCOMPtr<nsIRunnable> ev =
     new nsPluginCrashedEvent(thisContent,
@@ -3840,9 +3834,8 @@ nsObjectLoadingContent::SetupProtoChainR
   }
   nsObjectLoadingContent* objectLoadingContent =
     static_cast<nsObjectLoadingContent*>(mContent.get());
   objectLoadingContent->SetupProtoChain(cx, obj);
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(nsObjectLoadingContent::SetupProtoChainRunner, nsIRunnable)
-
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -22,25 +22,26 @@
 #include "nsIRunnable.h"
 #include "nsIThreadInternal.h"
 #include "nsIFrame.h"
 #include "nsIFrameLoader.h"
 
 class nsAsyncInstantiateEvent;
 class nsStopPluginRunnable;
 class AutoSetInstantiatingToFalse;
+class nsFrameLoader;
 class nsPluginFrame;
-class nsFrameLoader;
 class nsXULElement;
 class nsPluginInstanceOwner;
 
 namespace mozilla {
 namespace dom {
 template<typename T> class Sequence;
 struct MozPluginParameter;
+class HTMLIFrameElement;
 } // namespace dom
 } // namespace mozilla
 
 class nsObjectLoadingContent : public nsImageLoadingContent
                              , public nsIStreamListener
                              , public nsIFrameLoaderOwner
                              , public nsIObjectLoadingContent
                              , public nsIChannelEventSink
@@ -202,32 +203,38 @@ class nsObjectLoadingContent : public ns
     bool Activated() const
     {
       return mActivated;
     }
     nsIURI* GetSrcURI() const
     {
       return mURI;
     }
-  
+
     /**
      * The default state that this plugin would be without manual activation.
      * @returns PLUGIN_ACTIVE if the default state would be active.
      */
     uint32_t DefaultFallbackType();
 
     uint32_t PluginFallbackType() const
     {
       return mFallbackType;
     }
     bool HasRunningPlugin() const
     {
       return !!mInstanceOwner;
     }
-    void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aRv)
+    void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
+                          mozilla::ErrorResult& aRv)
+    {
+      aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    }
+    void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
+                          mozilla::ErrorResult& aRv)
     {
       aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     }
     void LegacyCall(JSContext* aCx, JS::Handle<JS::Value> aThisVal,
                     const mozilla::dom::Sequence<JS::Value>& aArguments,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aRv);
 
@@ -395,21 +402,21 @@ class nsObjectLoadingContent : public ns
      * - mURI                 : The final URI, considering mChannel if
      *                          mChannelLoaded is set
      * - mContentType         : The final content type, considering mChannel if
      *                          mChannelLoaded is set
      * - mBaseURI             : The object's base URI, which may be set by the
      *                          object (codebase attribute)
      * - mType                : The type the object is determined to be based
      *                          on the above
-     * 
+     *
      * NOTE The class assumes that mType is the currently loaded type at various
      *      points, so the caller of this function must take the appropriate
      *      actions to ensure this
-     * 
+     *
      * NOTE This function does not perform security checks, only determining the
      *      requested type and parameters of the object.
      *
      * @param aJavaURI Specify that the URI will be consumed by java, which
      *                 changes codebase parsing and URI construction. Used
      *                 internally.
      *
      * @return Returns a bitmask of ParameterUpdateFlags values
@@ -501,17 +508,17 @@ class nsObjectLoadingContent : public ns
     void NotifyStateChanged(ObjectType aOldType,
                             mozilla::EventStates aOldState,
                             bool aSync, bool aNotify);
 
     /**
      * Returns a ObjectType value corresponding to the type of content we would
      * support the given MIME type as, taking capabilities and plugin state
      * into account
-     * 
+     *
      * NOTE this does not consider whether the content would be suppressed by
      *      click-to-play or other content policy checks
      */
     ObjectType GetTypeOfContent(const nsCString& aMIMEType);
 
     /**
      * Gets the frame that's associated with this content node.
      * Does not flush.
--- a/dom/base/nsReferencedElement.cpp
+++ b/dom/base/nsReferencedElement.cpp
@@ -104,17 +104,17 @@ nsReferencedElement::Reset(nsIContent* a
       if (observer) {
         load->AddObserver(observer);
       }
       // Keep going so we set up our watching stuff a bit
     }
   }
 
   if (aWatch) {
-    nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
+    nsCOMPtr<nsIAtom> atom = NS_Atomize(ref);
     if (!atom)
       return;
     atom.swap(mWatchID);
   }
 
   mReferencingImage = aReferenceImage;
 
   HaveNewDocument(doc, aWatch, ref);
@@ -126,17 +126,17 @@ nsReferencedElement::ResetWithID(nsICont
 {
   nsIDocument *doc = aFromContent->OwnerDoc();
   if (!doc)
     return;
 
   // XXX Need to take care of XBL/XBL2
 
   if (aWatch) {
-    nsCOMPtr<nsIAtom> atom = do_GetAtom(aID);
+    nsCOMPtr<nsIAtom> atom = NS_Atomize(aID);
     if (!atom)
       return;
     atom.swap(mWatchID);
   }
 
   mReferencingImage = false;
 
   HaveNewDocument(doc, aWatch, aID);
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -20,16 +20,17 @@ support-files =
   file_bug1209621.xul
   fileconstructor_file.png
   frame_bug814638.xul
   frame_registerElement_content.html
   registerElement_ep.js
   host_bug814638.xul
   window_nsITextInputProcessor.xul
   title_window.xul
+  window_swapFrameLoaders.xul
 
 [test_bug120684.xul]
 [test_bug206691.xul]
 [test_bug339494.xul]
 [test_bug357450.xul]
 support-files = ../file_bug357450.js
 [test_bug380418.html]
 [test_bug380418.html^headers^]
@@ -66,8 +67,9 @@ skip-if = buildapp == 'mulet'
 [test_registerElement_content.xul]
 [test_registerElement_ep.xul]
 [test_domparsing.xul]
 [test_fileconstructor.xul]
 [test_fileconstructor_tempfile.xul]
 [test_nsITextInputProcessor.xul]
 [test_title.xul]
 [test_windowroot.xul]
+[test_swapFrameLoaders.xul]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_swapFrameLoaders.xul
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1242644
+Test swapFrameLoaders with different frame types and remoteness
+-->
+<window title="Mozilla Bug 1242644"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1242644"
+     target="_blank">Mozilla Bug 1242644</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.requestLongerTimeout(100);
+
+  window.open("window_swapFrameLoaders.xul", "bug1242644",
+              "chrome,width=600,height=600");
+  ]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/window_swapFrameLoaders.xul
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1242644
+Test swapFrameLoaders with different frame types and remoteness
+-->
+<window title="Mozilla Bug 1242644"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+
+  <script type="application/javascript"><![CDATA[
+  ["SimpleTest", "SpecialPowers", "info", "is"].forEach(key => {
+    window[key] = window.opener[key];
+  })
+  const { interfaces: Ci } = Components;
+
+  const NS = {
+    xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+    html: "http://www.w3.org/1999/xhtml",
+  }
+
+  const TAG = {
+    xul: "browser",
+    html: "iframe", // mozbrowser
+  }
+
+  const SCENARIOS = [
+    ["xul", "xul"],
+    ["xul", "html"],
+    ["html", "xul"],
+    ["html", "html"],
+    ["xul", "xul", "remote"],
+    ["xul", "html", "remote"],
+    ["html", "xul", "remote"],
+    ["html", "html", "remote"],
+  ];
+
+  function once(target, eventName, useCapture = false) {
+    info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+    return new Promise(resolve => {
+      for (let [add, remove] of [
+        ["addEventListener", "removeEventListener"],
+        ["addMessageListener", "removeMessageListener"],
+      ]) {
+        if ((add in target) && (remove in target)) {
+          target[add](eventName, function onEvent(...aArgs) {
+            info("Got event: '" + eventName + "' on " + target + ".");
+            target[remove](eventName, onEvent, useCapture);
+            resolve(aArgs);
+          }, useCapture);
+          break;
+        }
+      }
+    });
+  }
+
+  function* addFrame(type, remote) {
+    let frame = document.createElementNS(NS[type], TAG[type]);
+    frame.setAttribute("remote", remote);
+    if (remote && type == "xul") {
+      frame.setAttribute("style", "-moz-binding: none;");
+    }
+    if (type == "html") {
+      frame.setAttribute("mozbrowser", "true");
+      frame.setAttribute("noisolation", "true");
+      frame.setAttribute("allowfullscreen", "true");
+    } else if (type == "xul") {
+      frame.setAttribute("type", "content");
+    }
+    frame.setAttribute("src", "about:blank");
+    document.documentElement.appendChild(frame);
+    return frame;
+  }
+
+  add_task(function*() {
+    yield new Promise(resolve => {
+      SpecialPowers.pushPrefEnv(
+        { "set": [["dom.mozBrowserFramesEnabled", true]] },
+        resolve);
+    });
+  });
+
+  add_task(function*() {
+    for (let scenario of SCENARIOS) {
+      let [ typeA, typeB, remote ] = scenario;
+      remote = !!remote;
+      info(`Adding frame A, type ${typeA}, remote ${remote}`);
+      let frameA = yield addFrame(typeA, remote);
+
+      info(`Adding frame B, type ${typeB}, remote ${remote}`);
+      let frameB = yield addFrame(typeB, remote);
+
+      let frameScriptFactory = function(name) {
+        return `function() {
+          addMessageListener("ping", function() {
+            sendAsyncMessage("pong", "${name}");
+          });
+        }`;
+      }
+
+      // Load frame script into each frame
+      {
+        let mmA = frameA.frameLoader.messageManager;
+        let mmB = frameB.frameLoader.messageManager;
+
+        mmA.loadFrameScript("data:,(" + frameScriptFactory("A") + ")()", false);
+        mmB.loadFrameScript("data:,(" + frameScriptFactory("B") + ")()", false);
+      }
+
+      // Ping before swap
+      {
+        let mmA = frameA.frameLoader.messageManager;
+        let mmB = frameB.frameLoader.messageManager;
+
+        let inflightA = once(mmA, "pong");
+        let inflightB = once(mmB, "pong");
+
+        info("Ping message manager for frame A");
+        mmA.sendAsyncMessage("ping");
+        let [ { data: pongA } ] = yield inflightA;
+        is(pongA, "A", "Frame A message manager gets reply A before swap");
+
+        info("Ping message manager for frame B");
+        mmB.sendAsyncMessage("ping");
+        let [ { data: pongB } ] = yield inflightB;
+        is(pongB, "B", "Frame B message manager gets reply B before swap");
+      }
+
+      // Ping after swap using message managers acquired before
+      {
+        let mmA = frameA.frameLoader.messageManager;
+        let mmB = frameB.frameLoader.messageManager;
+
+        info("swapFrameLoaders");
+        frameA.swapFrameLoaders(frameB);
+
+        let inflightA = once(mmA, "pong");
+        let inflightB = once(mmB, "pong");
+
+        info("Ping message manager for frame A");
+        mmA.sendAsyncMessage("ping");
+        let [ { data: pongA } ] = yield inflightA;
+        is(pongA, "B", "Frame A message manager acquired before swap gets reply B after swap");
+
+        info("Ping message manager for frame B");
+        mmB.sendAsyncMessage("ping");
+        let [ { data: pongB } ] = yield inflightB;
+        is(pongB, "A", "Frame B message manager acquired before swap gets reply A after swap");
+      }
+
+      // Ping after swap using message managers acquired after
+      {
+        let mmA = frameA.frameLoader.messageManager;
+        let mmB = frameB.frameLoader.messageManager;
+
+        let inflightA = once(mmA, "pong");
+        let inflightB = once(mmB, "pong");
+
+        info("Ping message manager for frame A");
+        mmA.sendAsyncMessage("ping");
+        let [ { data: pongA } ] = yield inflightA;
+        is(pongA, "B", "Frame A message manager acquired after swap gets reply B after swap");
+
+        info("Ping message manager for frame B");
+        mmB.sendAsyncMessage("ping");
+        let [ { data: pongB } ] = yield inflightB;
+        is(pongB, "A", "Frame B message manager acquired after swap gets reply A after swap");
+      }
+
+      frameA.remove();
+      frameB.remove();
+    }
+  });
+  ]]></script>
+</window>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -688,46 +688,47 @@ CreateInterfaceObject(JSContext* cx, JS:
     }
 
     if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
       return nullptr;
     }
   }
 
   if (properties) {
-    if (properties->staticMethods &&
-        !DefinePrefable(cx, constructor, properties->staticMethods)) {
+    if (properties->HasStaticMethods() &&
+        !DefinePrefable(cx, constructor, properties->StaticMethods())) {
       return nullptr;
     }
 
-    if (properties->staticAttributes &&
-        !DefinePrefable(cx, constructor, properties->staticAttributes)) {
+    if (properties->HasStaticAttributes() &&
+        !DefinePrefable(cx, constructor, properties->StaticAttributes())) {
       return nullptr;
     }
 
-    if (properties->constants &&
-        !DefinePrefable(cx, constructor, properties->constants)) {
+    if (properties->HasConstants() &&
+        !DefinePrefable(cx, constructor, properties->Constants())) {
       return nullptr;
     }
   }
 
   if (chromeOnlyProperties) {
-    if (chromeOnlyProperties->staticMethods &&
-        !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
+    if (chromeOnlyProperties->HasStaticMethods() &&
+        !DefinePrefable(cx, constructor,
+                        chromeOnlyProperties->StaticMethods())) {
       return nullptr;
     }
 
-    if (chromeOnlyProperties->staticAttributes &&
+    if (chromeOnlyProperties->HasStaticAttributes() &&
         !DefinePrefable(cx, constructor,
-                        chromeOnlyProperties->staticAttributes)) {
+                        chromeOnlyProperties->StaticAttributes())) {
       return nullptr;
     }
 
-    if (chromeOnlyProperties->constants &&
-        !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
+    if (chromeOnlyProperties->HasConstants() &&
+        !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) {
       return nullptr;
     }
   }
 
   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
     return nullptr;
   }
 
@@ -802,45 +803,45 @@ CreateInterfacePrototypeObject(JSContext
 }
 
 bool
 DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
                  const NativeProperties* properties,
                  const NativeProperties* chromeOnlyProperties)
 {
   if (properties) {
-    if (properties->methods &&
-        !DefinePrefable(cx, obj, properties->methods)) {
+    if (properties->HasMethods() &&
+        !DefinePrefable(cx, obj, properties->Methods())) {
       return false;
     }
 
-    if (properties->attributes &&
-        !DefinePrefable(cx, obj, properties->attributes)) {
+    if (properties->HasAttributes() &&
+        !DefinePrefable(cx, obj, properties->Attributes())) {
       return false;
     }
 
-    if (properties->constants &&
-        !DefinePrefable(cx, obj, properties->constants)) {
+    if (properties->HasConstants() &&
+        !DefinePrefable(cx, obj, properties->Constants())) {
       return false;
     }
   }
 
   if (chromeOnlyProperties) {
-    if (chromeOnlyProperties->methods &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->methods)) {
+    if (chromeOnlyProperties->HasMethods() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
       return false;
     }
 
-    if (chromeOnlyProperties->attributes &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->attributes)) {
+    if (chromeOnlyProperties->HasAttributes() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
       return false;
     }
 
-    if (chromeOnlyProperties->constants &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->constants)) {
+    if (chromeOnlyProperties->HasConstants() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
       return false;
     }
   }
 
   return true;
 }
 
 void
@@ -854,26 +855,27 @@ CreateInterfaceObjects(JSContext* cx, JS
                        const NativeProperties* properties,
                        const NativeProperties* chromeOnlyProperties,
                        const char* name, bool defineOnGlobal,
                        const char* const* unscopableNames)
 {
   MOZ_ASSERT(protoClass || constructorClass || constructor,
              "Need at least one class or a constructor!");
   MOZ_ASSERT(!((properties &&
-                (properties->methods || properties->attributes)) ||
+                (properties->HasMethods() || properties->HasAttributes())) ||
                (chromeOnlyProperties &&
-                (chromeOnlyProperties->methods ||
-                 chromeOnlyProperties->attributes))) || protoClass,
+                (chromeOnlyProperties->HasMethods() ||
+                 chromeOnlyProperties->HasAttributes()))) || protoClass,
              "Methods or properties but no protoClass!");
   MOZ_ASSERT(!((properties &&
-                (properties->staticMethods || properties->staticAttributes)) ||
+                (properties->HasStaticMethods() ||
+                 properties->HasStaticAttributes())) ||
                (chromeOnlyProperties &&
-                (chromeOnlyProperties->staticMethods ||
-                 chromeOnlyProperties->staticAttributes))) ||
+                (chromeOnlyProperties->HasStaticMethods() ||
+                 chromeOnlyProperties->HasStaticAttributes()))) ||
              constructorClass || constructor,
              "Static methods but no constructorClass or constructor!");
   MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
              "Must have name precisely when we have an interface object");
   MOZ_ASSERT(!constructorClass || !constructor);
   MOZ_ASSERT(!protoClass == !protoCache,
              "If, and only if, there is an interface prototype object we need "
              "to cache it");
@@ -1185,17 +1187,18 @@ XrayCreateFunction(JSContext* cx, JS::Ha
                                 JS::ObjectValue(*obj));
 #endif
   return obj;
 }
 
 static bool
 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
                      JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                     const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
+                     const Prefable<const JSPropertySpec>* attributes,
+                     const jsid* attributeIds,
                      const JSPropertySpec* attributeSpecs,
                      JS::MutableHandle<JS::PropertyDescriptor> desc,
                      bool& cacheOnHolder)
 {
   for (; attributes->specs; ++attributes) {
     if (attributes->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
       // looking at now.
@@ -1235,17 +1238,17 @@ XrayResolveAttribute(JSContext* cx, JS::
   }
   return true;
 }
 
 static bool
 XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
                   JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                   const Prefable<const JSFunctionSpec>* methods,
-                  jsid* methodIds,
+                  const jsid* methodIds,
                   const JSFunctionSpec* methodSpecs,
                   JS::MutableHandle<JS::PropertyDescriptor> desc,
                   bool& cacheOnHolder)
 {
   const Prefable<const JSFunctionSpec>* method;
   for (method = methods; method->specs; ++method) {
     if (method->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
@@ -1296,35 +1299,35 @@ XrayResolveUnforgeableProperty(JSContext
                                JS::MutableHandle<JS::PropertyDescriptor> desc,
                                bool& cacheOnHolder,
                                const NativeProperties* nativeProperties)
 {
   if (!nativeProperties) {
     return true;
   }
 
-  if (nativeProperties->unforgeableAttributes) {
+  if (nativeProperties->HasUnforgeableAttributes()) {
     if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                              nativeProperties->unforgeableAttributes,
-                              nativeProperties->unforgeableAttributeIds,
-                              nativeProperties->unforgeableAttributeSpecs,
+                              nativeProperties->UnforgeableAttributes(),
+                              nativeProperties->UnforgeableAttributeIds(),
+                              nativeProperties->UnforgeableAttributeSpecs(),
                               desc, cacheOnHolder)) {
       return false;
     }
 
     if (desc.object()) {
       return true;
     }
   }
 
-  if (nativeProperties->unforgeableMethods) {
+  if (nativeProperties->HasUnforgeableMethods()) {
     if (!XrayResolveMethod(cx, wrapper, obj, id,
-                           nativeProperties->unforgeableMethods,
-                           nativeProperties->unforgeableMethodIds,
-                           nativeProperties->unforgeableMethodSpecs,
+                           nativeProperties->UnforgeableMethods(),
+                           nativeProperties->UnforgeableMethodIds(),
+                           nativeProperties->UnforgeableMethodSpecs(),
                            desc, cacheOnHolder)) {
       return false;
     }
 
     if (desc.object()) {
       return true;
     }
   }
@@ -1334,89 +1337,95 @@ XrayResolveUnforgeableProperty(JSContext
 
 static bool
 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                     JS::MutableHandle<JS::PropertyDescriptor> desc,
                     bool& cacheOnHolder, DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
-  const Prefable<const JSFunctionSpec>* methods;
-  jsid* methodIds;
-  const JSFunctionSpec* methodSpecs;
+  bool hasMethods = false;
   if (type == eInterface) {
-    methods = nativeProperties->staticMethods;
-    methodIds = nativeProperties->staticMethodIds;
-    methodSpecs = nativeProperties->staticMethodSpecs;
+    hasMethods = nativeProperties->HasStaticMethods();
   } else {
-    methods = nativeProperties->methods;
-    methodIds = nativeProperties->methodIds;
-    methodSpecs = nativeProperties->methodSpecs;
+    hasMethods = nativeProperties->HasMethods();
   }
-  if (methods) {
+  if (hasMethods) {
+    const Prefable<const JSFunctionSpec>* methods;
+    const jsid* methodIds;
+    const JSFunctionSpec* methodSpecs;
+    if (type == eInterface) {
+      methods = nativeProperties->StaticMethods();
+      methodIds = nativeProperties->StaticMethodIds();
+      methodSpecs = nativeProperties->StaticMethodSpecs();
+    } else {
+      methods = nativeProperties->Methods();
+      methodIds = nativeProperties->MethodIds();
+      methodSpecs = nativeProperties->MethodSpecs();
+    }
     JS::Rooted<jsid> methodId(cx);
     if (nativeProperties->iteratorAliasMethodIndex != -1 &&
         id == SYMBOL_TO_JSID(
                 JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) {
       methodId =
-        nativeProperties->methodIds[nativeProperties->iteratorAliasMethodIndex];
+        nativeProperties->MethodIds()[nativeProperties->iteratorAliasMethodIndex];
     } else {
       methodId = id;
     }
     if (!XrayResolveMethod(cx, wrapper, obj, methodId, methods, methodIds,
                            methodSpecs, desc, cacheOnHolder)) {
       return false;
     }
     if (desc.object()) {
       return true;
     }
   }
 
   if (type == eInterface) {
-    if (nativeProperties->staticAttributes) {
+    if (nativeProperties->HasStaticAttributes()) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                                nativeProperties->staticAttributes,
-                                nativeProperties->staticAttributeIds,
-                                nativeProperties->staticAttributeSpecs, desc,
-                                cacheOnHolder)) {
+                                nativeProperties->StaticAttributes(),
+                                nativeProperties->StaticAttributeIds(),
+                                nativeProperties->StaticAttributeSpecs(),
+                                desc, cacheOnHolder)) {
         return false;
       }
       if (desc.object()) {
         return true;
       }
     }
   } else {
-    if (nativeProperties->attributes) {
+    if (nativeProperties->HasAttributes()) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                                nativeProperties->attributes,
-                                nativeProperties->attributeIds,
-                                nativeProperties->attributeSpecs, desc,
-                                cacheOnHolder)) {
+                                nativeProperties->Attributes(),
+                                nativeProperties->AttributeIds(),
+                                nativeProperties->AttributeSpecs(),
+                                desc, cacheOnHolder)) {
         return false;
       }
       if (desc.object()) {
         return true;
       }
     }
   }
 
-  if (nativeProperties->constants) {
+  if (nativeProperties->HasConstants()) {
     const Prefable<const ConstantSpec>* constant;
-    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+    for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
       if (constant->isEnabled(cx, obj)) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
-        size_t i = constant->specs - nativeProperties->constantSpecs;
-        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
-          if (id == nativeProperties->constantIds[i]) {
+        size_t i = constant->specs - nativeProperties->ConstantSpecs();
+        for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+          if (id == nativeProperties->ConstantIds()[i]) {
             cacheOnHolder = true;
 
             desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
             desc.object().set(wrapper);
-            desc.value().set(nativeProperties->constantSpecs[i].value);
+            desc.value().set(nativeProperties->ConstantSpecs()[i].value);
             return true;
           }
         }
       }
     }
   }
 
   return true;
@@ -1630,17 +1639,17 @@ XrayDefineProperty(JSContext* cx, JS::Ha
   return handler->defineProperty(cx, wrapper, id, desc, result, defined);
 }
 
 template<typename SpecType>
 bool
 XrayAttributeOrMethodKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
                           JS::Handle<JSObject*> obj,
                           const Prefable<const SpecType>* list,
-                          jsid* ids, const SpecType* specList,
+                          const jsid* ids, const SpecType* specList,
                           unsigned flags, JS::AutoIdVector& props)
 {
   for (; list->specs; ++list) {
     if (list->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
       // looking at now.
       size_t i = list->specs - specList;
       for ( ; ids[i] != JSID_VOID; ++i) {
@@ -1653,62 +1662,62 @@ XrayAttributeOrMethodKeys(JSContext* cx,
           return false;
         }
       }
     }
   }
   return true;
 }
 
-#define ADD_KEYS_IF_DEFINED(fieldName) {                                      \
-  if (nativeProperties->fieldName##s &&                                       \
+#define ADD_KEYS_IF_DEFINED(FieldName) {                                      \
+  if (nativeProperties->Has##FieldName##s() &&                                \
       !XrayAttributeOrMethodKeys(cx, wrapper, obj,                            \
-                                 nativeProperties->fieldName##s,              \
-                                 nativeProperties->fieldName##Ids,            \
-                                 nativeProperties->fieldName##Specs,          \
+                                 nativeProperties->FieldName##s(),            \
+                                 nativeProperties->FieldName##Ids(),          \
+                                 nativeProperties->FieldName##Specs(),        \
                                  flags, props)) {                             \
     return false;                                                             \
   }                                                                           \
 }
 
 
 bool
 XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj,
                     unsigned flags, JS::AutoIdVector& props,
                     DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
   MOZ_ASSERT(type != eNamedPropertiesObject);
 
   if (IsInstance(type)) {
-    ADD_KEYS_IF_DEFINED(unforgeableMethod);
-    ADD_KEYS_IF_DEFINED(unforgeableAttribute);
+    ADD_KEYS_IF_DEFINED(UnforgeableMethod);
+    ADD_KEYS_IF_DEFINED(UnforgeableAttribute);
     if (type == eGlobalInstance) {
-      ADD_KEYS_IF_DEFINED(method);
-      ADD_KEYS_IF_DEFINED(attribute);
+      ADD_KEYS_IF_DEFINED(Method);
+      ADD_KEYS_IF_DEFINED(Attribute);
     }
   } else if (type == eInterface) {
-    ADD_KEYS_IF_DEFINED(staticMethod);
-    ADD_KEYS_IF_DEFINED(staticAttribute);
+    ADD_KEYS_IF_DEFINED(StaticMethod);
+    ADD_KEYS_IF_DEFINED(StaticAttribute);
   } else if (type != eGlobalInterfacePrototype) {
     MOZ_ASSERT(IsInterfacePrototype(type));
-    ADD_KEYS_IF_DEFINED(method);
-    ADD_KEYS_IF_DEFINED(attribute);
+    ADD_KEYS_IF_DEFINED(Method);
+    ADD_KEYS_IF_DEFINED(Attribute);
   }
 
-  if (nativeProperties->constants) {
+  if (nativeProperties->HasConstants()) {
     const Prefable<const ConstantSpec>* constant;
-    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+    for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
       if (constant->isEnabled(cx, obj)) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
-        size_t i = constant->specs - nativeProperties->constantSpecs;
-        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
-          if (!props.append(nativeProperties->constantIds[i])) {
+        size_t i = constant->specs - nativeProperties->ConstantSpecs();
+        for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+          if (!props.append(nativeProperties->ConstantIds()[i])) {
             return false;
           }
         }
       }
     }
   }
 
   return true;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -327,21 +327,21 @@ class CGNativePropertyHooks(CGThing):
             enumerateOwnProperties = "EnumerateOwnProperties"
         elif self.descriptor.needsXrayResolveHooks():
             resolveOwnProperty = "ResolveOwnPropertyViaResolve"
             enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
         else:
             resolveOwnProperty = "nullptr"
             enumerateOwnProperties = "nullptr"
         if self.properties.hasNonChromeOnly():
-            regular = "&sNativeProperties"
+            regular = "sNativeProperties.Upcast()"
         else:
             regular = "nullptr"
         if self.properties.hasChromeOnly():
-            chrome = "&sChromeOnlyNativeProperties"
+            chrome = "sChromeOnlyNativeProperties.Upcast()"
         else:
             chrome = "nullptr"
         constructorID = "constructors::id::"
         if self.descriptor.interface.hasInterfaceObject():
             constructorID += self.descriptor.name
         else:
             constructorID += "_ID_Count"
         prototypeID = "prototypes::id::"
@@ -2648,38 +2648,54 @@ class PropertyArrays():
 
 
 class CGNativeProperties(CGList):
     def __init__(self, descriptor, properties):
         def generateNativeProperties(name, chrome):
             def check(p):
                 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
 
-            nativeProps = []
-            for array in properties.arrayNames():
-                propertyArray = getattr(properties, array)
-                if check(propertyArray):
-                    if propertyArray.usedForXrays():
-                        ids = "%(name)s_ids"
-                    else:
-                        ids = "nullptr"
-                    props = "%(name)s, " + ids + ", %(name)s_specs"
-                    props = (props % {'name': propertyArray.variableName(chrome)})
-                else:
-                    props = "nullptr, nullptr, nullptr"
-                nativeProps.append(CGGeneric(props))
+            nativePropsInts = []
+            nativePropsTrios = []
+
             iteratorAliasIndex = -1
             for index, item in enumerate(properties.methods.regular):
                 if item.get("hasIteratorAlias"):
                     iteratorAliasIndex = index
                     break
-            nativeProps.append(CGGeneric(str(iteratorAliasIndex)))
+            nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
+
+            offset = 0
+            for array in properties.arrayNames():
+                propertyArray = getattr(properties, array)
+                if check(propertyArray):
+                    varName = propertyArray.variableName(chrome)
+                    bitfields = "true,  %d /* %s */" % (offset, varName)
+                    offset += 1
+                    nativePropsInts.append(CGGeneric(bitfields))
+
+                    if propertyArray.usedForXrays():
+                        ids = "%(name)s_ids"
+                    else:
+                        ids = "nullptr"
+                    trio = "{ %(name)s, " + ids + ", %(name)s_specs }"
+                    trio = trio % {'name': varName}
+                    nativePropsTrios.append(CGGeneric(trio))
+                else:
+                    bitfields = "false, 0"
+                    nativePropsInts.append(CGGeneric(bitfields))
+
+            nativePropsTrios = \
+                [CGWrapper(CGIndenter(CGList(nativePropsTrios, ",\n")),
+                           pre='{\n', post='\n}')]
+            nativeProps = nativePropsInts + nativePropsTrios
+            pre = ("static const NativePropertiesN<%d> %s = {\n" %
+                   (offset, name))
             return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
-                             pre="static const NativeProperties %s = {\n" % name,
-                             post="\n};\n")
+                             pre=pre, post="\n};\n")
 
         nativeProperties = []
         if properties.hasNonChromeOnly():
             nativeProperties.append(
                 generateNativeProperties("sNativeProperties", False))
         if properties.hasChromeOnly():
             nativeProperties.append(
                 generateNativeProperties("sChromeOnlyNativeProperties", True))
@@ -2856,21 +2872,21 @@ class CGCreateInterfaceObjectsMethod(CGA
         else:
             # We don't have slots to store the named constructors.
             assert len(self.descriptor.interface.namedConstructors) == 0
             interfaceClass = "nullptr"
             interfaceCache = "nullptr"
 
         isGlobal = self.descriptor.isGlobal() is not None
         if not isGlobal and self.properties.hasNonChromeOnly():
-            properties = "&sNativeProperties"
+            properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if not isGlobal and self.properties.hasChromeOnly():
-            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
+            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
         else:
             chromeProperties = "nullptr"
 
         call = fill(
             """
             JS::Heap<JSObject*>* protoCache = ${protoCache};
             JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
             dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
@@ -3737,21 +3753,21 @@ class CGWrapGlobalMethod(CGAbstractMetho
                 Argument('bool', 'aInitStandardClasses'),
                 Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.descriptor = descriptor
         self.properties = properties
 
     def definition_body(self):
         if self.properties.hasNonChromeOnly():
-            properties = "&sNativeProperties"
+            properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
-            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
+            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
         else:
             chromeProperties = "nullptr"
 
         if self.descriptor.workers:
             fireOnNewGlobal = """// XXXkhuey can't do this yet until workers can lazy resolve.
 // JS_FireOnNewGlobalObject(aCx, aReflector);
 """
         else:
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -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/. */
 
 #ifndef mozilla_dom_DOMJSClass_h
 #define mozilla_dom_DOMJSClass_h
 
 #include "jsfriendapi.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 
 #include "mozilla/dom/PrototypeList.h" // auto-generated
 
 #include "mozilla/dom/JSSlots.h"
 
 class nsCycleCollectionParticipant;
 
@@ -153,49 +154,110 @@ struct Prefable {
   PrefableDisablers* const disablers;
 
   // Array of specs, terminated in whatever way is customary for T.
   // Null to indicate a end-of-array for Prefable, when such an
   // indicator is needed.
   const T* const specs;
 };
 
-struct NativeProperties
-{
-  const Prefable<const JSFunctionSpec>* staticMethods;
-  jsid* staticMethodIds;
-  const JSFunctionSpec* staticMethodSpecs;
+// Conceptually, NativeProperties has seven (Prefable<T>*, jsid*, T*) trios
+// (where T is one of JSFunctionSpec, JSPropertySpec, or ConstantSpec), one for
+// each of: static methods and attributes, methods and attributes, unforgeable
+// methods and attributes, and constants.
+//
+// That's 21 pointers, but in most instances most of the trios are all null,
+// and there are many instances. To save space we use a variable-length type,
+// NativePropertiesN<N>, to hold the data and getters to access it. It has N
+// actual trios (stored in trios[]), plus four bits for each of the 7 possible
+// trios: 1 bit that states if that trio is present, and 3 that state that
+// trio's offset (if present) in trios[].
+//
+// All trio accesses should be done via the getters, which contain assertions
+// that check we don't overrun the end of the struct. (The trio data members are
+// public only so they can be statically initialized.) These assertions should
+// never fail so long as (a) accesses to the variable-length part are guarded by
+// appropriate Has*() calls, and (b) all instances are well-formed, i.e. the
+// value of N matches the number of mHas* members that are true.
+//
+// Finally, we define a typedef of NativePropertiesN<7>, NativeProperties, which
+// we use as a "base" type used to refer to all instances of NativePropertiesN.
+// (7 is used because that's the maximum valid parameter, though any other
+// value 1..6 could also be used.) This is reasonable because of the
+// aforementioned assertions in the getters. Upcast() is used to convert
+// specific instances to this "base" type.
+//
+template <int N>
+struct NativePropertiesN {
+  // Trio structs are stored in the trios[] array, and each element in the
+  // array could require a different T. Therefore, we can't use the correct
+  // type for mPrefables and mSpecs. Instead we use void* and cast to the
+  // correct type in the getters.
+  struct Trio {
+    const /*Prefable<const T>*/ void* const mPrefables;
+    const jsid* const mIds;
+    const /*T*/ void* const mSpecs;
+  };
 
-  const Prefable<const JSPropertySpec>* staticAttributes;
-  jsid* staticAttributeIds;
-  const JSPropertySpec* staticAttributeSpecs;
+  const int32_t iteratorAliasMethodIndex;
 
-  const Prefable<const JSFunctionSpec>* methods;
-  jsid* methodIds;
-  const JSFunctionSpec* methodSpecs;
+  MOZ_CONSTEXPR const NativePropertiesN<7>* Upcast() const {
+    return reinterpret_cast<const NativePropertiesN<7>*>(this);
+  }
 
-  const Prefable<const JSPropertySpec>* attributes;
-  jsid* attributeIds;
-  const JSPropertySpec* attributeSpecs;
-
-  const Prefable<const JSFunctionSpec>* unforgeableMethods;
-  jsid* unforgeableMethodIds;
-  const JSFunctionSpec* unforgeableMethodSpecs;
+#define DO(SpecT, FieldName) \
+public: \
+  /* The bitfields indicating the trio's presence and (if present) offset. */ \
+  const uint32_t mHas##FieldName##s:1; \
+  const uint32_t m##FieldName##sOffset:3; \
+private: \
+  const Trio* FieldName##sTrio() const { \
+    MOZ_ASSERT(Has##FieldName##s()); \
+    return &trios[m##FieldName##sOffset]; \
+  } \
+public: \
+  bool Has##FieldName##s() const { \
+    return mHas##FieldName##s; \
+  } \
+  const Prefable<const SpecT>* FieldName##s() const { \
+    return static_cast<const Prefable<const SpecT>*> \
+                      (FieldName##sTrio()->mPrefables); \
+  } \
+  const jsid* FieldName##Ids() const { \
+    return FieldName##sTrio()->mIds; \
+  } \
+  const SpecT* FieldName##Specs() const { \
+    return static_cast<const SpecT*>(FieldName##sTrio()->mSpecs); \
+  }
 
-  const Prefable<const JSPropertySpec>* unforgeableAttributes;
-  jsid* unforgeableAttributeIds;
-  const JSPropertySpec* unforgeableAttributeSpecs;
+  DO(JSFunctionSpec, StaticMethod)
+  DO(JSPropertySpec, StaticAttribute)
+  DO(JSFunctionSpec, Method)
+  DO(JSPropertySpec, Attribute)
+  DO(JSFunctionSpec, UnforgeableMethod)
+  DO(JSPropertySpec, UnforgeableAttribute)
+  DO(ConstantSpec,   Constant)
+
+#undef DO
+
+  const Trio trios[N];
+};
 
-  const Prefable<const ConstantSpec>* constants;
-  jsid* constantIds;
-  const ConstantSpec* constantSpecs;
+// Ensure the struct has the expected size. The 8 is for the
+// iteratorAliasMethodIndex plus the bitfields; the rest is for trios[].
+static_assert(sizeof(NativePropertiesN<1>) == 8 +  3*sizeof(void*), "1 size");
+static_assert(sizeof(NativePropertiesN<2>) == 8 +  6*sizeof(void*), "2 size");
+static_assert(sizeof(NativePropertiesN<3>) == 8 +  9*sizeof(void*), "3 size");
+static_assert(sizeof(NativePropertiesN<4>) == 8 + 12*sizeof(void*), "4 size");
+static_assert(sizeof(NativePropertiesN<5>) == 8 + 15*sizeof(void*), "5 size");
+static_assert(sizeof(NativePropertiesN<6>) == 8 + 18*sizeof(void*), "6 size");
+static_assert(sizeof(NativePropertiesN<7>) == 8 + 21*sizeof(void*), "7 size");
 
-  // Index into methods for the entry that is [Alias="@@iterator"], -1 if none
-  int32_t iteratorAliasMethodIndex;
-};
+// The "base" type.
+typedef NativePropertiesN<7> NativeProperties;
 
 struct NativePropertiesHolder
 {
   const NativeProperties* regular;
   const NativeProperties* chromeOnly;
 };
 
 // Helper structure for Xrays for DOM binding objects. The same instance is used
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -87,10 +87,10 @@ MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMET
 MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
 MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
 MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
 MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
 MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
 MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
 MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
-MSG_DEF(MSG_INVALID_DURATION_ERROR, 0, JSEXN_TYPEERR, "Invalid duration.")
-MSG_DEF(MSG_INVALID_EASING_ERROR, 0, JSEXN_TYPEERR, "Invalid easing.")
\ No newline at end of file
+MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
+MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
\ No newline at end of file
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -142,22 +142,22 @@ public:
 
     aResolver->Resolve(rv);
   }
 
 private:
   nsTArray<nsID> mDeletedBodyIdList;
 };
 
-bool IsHeadRequest(CacheRequest aRequest, CacheQueryParams aParams)
+bool IsHeadRequest(const CacheRequest& aRequest, const CacheQueryParams& aParams)
 {
   return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head");
 }
 
-bool IsHeadRequest(CacheRequestOrVoid aRequest, CacheQueryParams aParams)
+bool IsHeadRequest(const CacheRequestOrVoid& aRequest, const CacheQueryParams& aParams)
 {
   if (aRequest.type() == CacheRequestOrVoid::TCacheRequest) {
     return !aParams.ignoreMethod() &&
            aRequest.get_CacheRequest().method().LowerCaseEqualsLiteral("head");
   }
   return false;
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1176,17 +1176,17 @@ CanvasRenderingContext2D::Redraw(const g
   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
 
   mCanvasElement->InvalidateCanvasContent(&aR);
 }
 
 void
 CanvasRenderingContext2D::DidRefresh()
 {
-  if (IsTargetValid() && SkiaGLTex()) {
+  if (IsTargetValid() && mIsSkiaGL) {
     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
     MOZ_ASSERT(glue);
 
     auto gl = glue->GetGLContext();
     gl->FlushIfHeavyGLCallsSinceLastFlush();
   }
 }
 
@@ -5661,30 +5661,29 @@ CanvasRenderingContext2D::GetCanvasLayer
 
   if (!mResetLayer && aOldLayer) {
     CanvasRenderingContext2DUserData* userData =
       static_cast<CanvasRenderingContext2DUserData*>(
         aOldLayer->GetUserData(&g2DContextLayerUserData));
 
     CanvasLayer::Data data;
 
-    GLuint skiaGLTex = SkiaGLTex();
-    if (mIsSkiaGL && skiaGLTex) {
-      SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
-      MOZ_ASSERT(glue);
-
-      data.mGLContext = glue->GetGLContext();
-      data.mFrontbufferGLTex = skiaGLTex;
-      PersistentBufferProvider *provider = GetBufferProvider(aManager);
-      data.mBufferProvider = provider;
-    } else {
-      PersistentBufferProvider *provider = GetBufferProvider(aManager);
-      data.mBufferProvider = provider;
+    if (mIsSkiaGL) {
+      GLuint skiaGLTex = SkiaGLTex();
+      if (skiaGLTex) {
+        SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+        MOZ_ASSERT(glue);
+        data.mGLContext = glue->GetGLContext();
+        data.mFrontbufferGLTex = skiaGLTex;
+      }
     }
 
+    PersistentBufferProvider *provider = GetBufferProvider(aManager);
+    data.mBufferProvider = provider;
+
     if (userData &&
         userData->IsForContext(this) &&
         static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
       RefPtr<Layer> ret = aOldLayer;
       return ret.forget();
     }
   }
 
@@ -5715,29 +5714,29 @@ CanvasRenderingContext2D::GetCanvasLayer
 
   CanvasLayer::Data data;
   data.mSize = nsIntSize(mWidth, mHeight);
   data.mHasAlpha = !mOpaque;
 
   canvasLayer->SetPreTransactionCallback(
           CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
 
-  GLuint skiaGLTex = SkiaGLTex();
-  if (mIsSkiaGL && skiaGLTex) {
-    SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
-    MOZ_ASSERT(glue);
-
-    data.mGLContext = glue->GetGLContext();
-    data.mFrontbufferGLTex = skiaGLTex;
-    PersistentBufferProvider *provider = GetBufferProvider(aManager);
-    data.mBufferProvider = provider;
-  } else {
-    PersistentBufferProvider *provider = GetBufferProvider(aManager);
-    data.mBufferProvider = provider;
-  }
+
+  if (mIsSkiaGL) {
+      GLuint skiaGLTex = SkiaGLTex();
+      if (skiaGLTex) {
+        SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+        MOZ_ASSERT(glue);
+        data.mGLContext = glue->GetGLContext();
+        data.mFrontbufferGLTex = skiaGLTex;
+      }
+  }
+
+  PersistentBufferProvider *provider = GetBufferProvider(aManager);
+  data.mBufferProvider = provider;
 
   canvasLayer->Initialize(data);
   uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
   canvasLayer->SetContentFlags(flags);
   canvasLayer->Updated();
 
   mResetLayer = false;
 
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/IDBCursor.h"
 #include "mozilla/dom/IDBObjectStore.h"
 #include "mozilla/dom/IDBRequest.h"
 #include "mozilla/dom/IDBTransaction.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/unused.h"
 
 #include "mozIApplication.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "nsIAppsService.h"
 #include "nsIDOMEvent.h"
 #include "nsIDocument.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
@@ -405,17 +406,19 @@ public:
       mTxn = aDb->Transaction();
 
       RefPtr<IDBObjectStore> store =
       mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), error);
       if (NS_WARN_IF(error.Failed())) {
         return;
       }
 
-      mRequest = store->OpenCursor(IDBCursorDirection::Prev, error);
+      AutoJSAPI jsapi;
+      jsapi.Init();
+      mRequest = store->OpenCursor(jsapi.cx(), IDBCursorDirection::Prev, error);
       if (NS_WARN_IF(error.Failed())) {
         return;
       }
 
       nsresult rv;
       rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
                                                    this, false);
       if (NS_FAILED(rv)) {
--- a/dom/events/CommandEvent.cpp
+++ b/dom/events/CommandEvent.cpp
@@ -48,17 +48,17 @@ CommandEvent::GetCommand(nsAString& aCom
 NS_IMETHODIMP
 CommandEvent::InitCommandEvent(const nsAString& aTypeArg,
                                bool aCanBubbleArg,
                                bool aCancelableArg,
                                const nsAString& aCommand)
 {
   Event::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
 
-  mEvent->AsCommandEvent()->command = do_GetAtom(aCommand);
+  mEvent->AsCommandEvent()->command = NS_Atomize(aCommand);
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -157,17 +157,17 @@ DataTransfer::~DataTransfer()
 // static
 already_AddRefed<DataTransfer>
 DataTransfer::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aEventType, bool aIsExternal,
                           ErrorResult& aRv)
 {
   nsAutoCString onEventType("on");
   AppendUTF16toUTF8(aEventType, onEventType);
-  nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(onEventType);
+  nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(onEventType);
   if (!eventTypeAtom) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
   EventMessage eventMessage = nsContentUtils::GetEventMessage(eventTypeAtom);
   RefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
                                                      eventMessage, aIsExternal,
--- a/dom/events/DragEvent.cpp
+++ b/dom/events/DragEvent.cpp
@@ -55,17 +55,17 @@ DragEvent::InitDragEvent(const nsAString
                          EventTarget* aRelatedTarget,
                          DataTransfer* aDataTransfer)
 {
   MouseEvent::InitMouseEvent(aType, aCanBubble, aCancelable,
                              aView, aDetail, aScreenX, aScreenY,
                              aClientX, aClientY, aCtrlKey, aAltKey,
                              aShiftKey, aMetaKey, aButton, aRelatedTarget);
   if (mEventIsInternal && mEvent) {
-    mEvent->AsDragEvent()->dataTransfer = aDataTransfer;
+    mEvent->AsDragEvent()->mDataTransfer = aDataTransfer;
   }
 }
 
 NS_IMETHODIMP
 DragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
 {
   NS_IF_ADDREF(*aDataTransfer = GetDataTransfer());
   return NS_OK;
@@ -85,17 +85,17 @@ DragEvent::GetDataTransfer()
 
   WidgetDragEvent* dragEvent = mEvent->AsDragEvent();
   // for synthetic events, just use the supplied data transfer object even if null
   if (!mEventIsInternal) {
     nsresult rv = nsContentUtils::SetDataTransferInEvent(dragEvent);
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
 
-  return dragEvent->dataTransfer;
+  return dragEvent->mDataTransfer;
 }
 
 // static
 already_AddRefed<DragEvent>
 DragEvent::Constructor(const GlobalObject& aGlobal,
                        const nsAString& aType,
                        const DragEventInit& aParam,
                        ErrorResult& aRv)
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -161,17 +161,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Ev
       case eMouseScrollEventClass:
       case eWheelEventClass:
       case eSimpleGestureEventClass:
       case ePointerEventClass:
         tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr;
         break;
       case eDragEventClass: {
         WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
-        dragEvent->dataTransfer = nullptr;
+        dragEvent->mDataTransfer = nullptr;
         dragEvent->relatedTarget = nullptr;
         break;
       }
       case eClipboardEventClass:
         tmp->mEvent->AsClipboardEvent()->mClipboardData = nullptr;
         break;
       case eMutationEventClass:
         tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr;
@@ -200,18 +200,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
       case eWheelEventClass:
       case eSimpleGestureEventClass:
       case ePointerEventClass:
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
         cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget);
         break;
       case eDragEventClass: {
         WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
-        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer");
-        cb.NoteXPCOMChild(dragEvent->dataTransfer);
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer");
+        cb.NoteXPCOMChild(dragEvent->mDataTransfer);
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
         cb.NoteXPCOMChild(dragEvent->relatedTarget);
         break;
       }
       case eClipboardEventClass:
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClipboardData");
         cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->mClipboardData);
         break;
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -684,29 +684,29 @@ EventListenerManager::ListenerCanHandle(
 
 void
 EventListenerManager::AddEventListenerByType(
                         const EventListenerHolder& aListenerHolder,
                         const nsAString& aType,
                         const EventListenerFlags& aFlags)
 {
   nsCOMPtr<nsIAtom> atom =
-    mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
+    mIsMainThreadELM ? NS_Atomize(NS_LITERAL_STRING("on") + aType) : nullptr;
   EventMessage message = nsContentUtils::GetEventMessage(atom);
   AddEventListenerInternal(aListenerHolder, message, atom, aType, aFlags);
 }
 
 void
 EventListenerManager::RemoveEventListenerByType(
                         const EventListenerHolder& aListenerHolder,
                         const nsAString& aType,
                         const EventListenerFlags& aFlags)
 {
   nsCOMPtr<nsIAtom> atom =
-    mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
+    mIsMainThreadELM ? NS_Atomize(NS_LITERAL_STRING("on") + aType) : nullptr;
   EventMessage message = nsContentUtils::GetEventMessage(atom);
   RemoveEventListenerInternal(aListenerHolder, message, atom, aType, aFlags);
 }
 
 EventListenerManager::Listener*
 EventListenerManager::FindEventHandler(EventMessage aEventMessage,
                                        nsIAtom* aTypeAtom,
                                        const nsAString& aTypeString)
@@ -1408,17 +1408,17 @@ EventListenerManager::MutationListenerBi
   }
   return bits;
 }
 
 bool
 EventListenerManager::HasListenersFor(const nsAString& aEventName)
 {
   if (mIsMainThreadELM) {
-    nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
+    nsCOMPtr<nsIAtom> atom = NS_Atomize(NS_LITERAL_STRING("on") + aEventName);
     return HasListenersFor(atom);
   }
 
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; ++i) {
     Listener* listener = &mListeners.ElementAt(i);
     if (listener->mTypeString == aEventName) {
       return true;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1595,17 +1595,17 @@ EventStateManager::BeginTrackingDragGest
     inDownFrame->GetContentForEvent(inDownEvent,
                                     getter_AddRefs(mGestureDownContent));
 
     mGestureDownFrameOwner = inDownFrame->GetContent();
     if (!mGestureDownFrameOwner) {
       mGestureDownFrameOwner = mGestureDownContent;
     }
   }
-  mGestureModifiers = inDownEvent->modifiers;
+  mGestureModifiers = inDownEvent->mModifiers;
   mGestureDownButtons = inDownEvent->buttons;
 
   if (Prefs::ClickHoldContextMenu()) {
     // fire off a timer to track click-hold
     CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
   }
 }
 
@@ -1634,17 +1634,17 @@ EventStateManager::FillInEventFromGestur
 {
   NS_ASSERTION(aEvent->widget == mCurrentTarget->GetNearestWidget(),
                "Incorrect widget in event");
 
   // Set the coordinates in the new event to the coordinates of
   // the old event, adjusted for the fact that the widget might be
   // different
   aEvent->refPoint = mGestureDownPoint - aEvent->widget->WidgetToScreenOffset();
-  aEvent->modifiers = mGestureModifiers;
+  aEvent->mModifiers = mGestureModifiers;
   aEvent->buttons = mGestureDownButtons;
 }
 
 //
 // GenerateDragGesture
 //
 // If we're in the TRACKING state of the d&d gesture tracker, check the current position
 // of the mouse in relation to the old one. If we've moved a sufficient amount from
@@ -1737,17 +1737,17 @@ EventStateManager::GenerateDragGesture(n
       // get the widget from the target frame
       WidgetDragEvent startEvent(aEvent->IsTrusted(), eDragStart, widget);
       FillInEventFromGestureDown(&startEvent);
 
       WidgetDragEvent gestureEvent(aEvent->IsTrusted(),
                                    eLegacyDragGesture, widget);
       FillInEventFromGestureDown(&gestureEvent);
 
-      startEvent.dataTransfer = gestureEvent.dataTransfer = dataTransfer;
+      startEvent.mDataTransfer = gestureEvent.mDataTransfer = dataTransfer;
       startEvent.inputSource = gestureEvent.inputSource = aEvent->inputSource;
 
       // Dispatch to the DOM. By setting mCurrentTarget we are faking
       // out the ESM and telling it that the current target frame is
       // actually where the mouseDown occurred, otherwise it will use
       // the frame the mouse is currently over which may or may not be
       // the same. (Note: saari and I have decided that we don't have
       // to reset |mCurrentTarget| when we're through because no one
@@ -2296,17 +2296,17 @@ EventStateManager::SendLineScrollEvent(n
   WidgetMouseScrollEvent event(aEvent->IsTrusted(),
                                eLegacyMouseLineOrPageScroll, aEvent->widget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.mTime = aEvent->mTime;
   event.mTimeStamp = aEvent->mTimeStamp;
-  event.modifiers = aEvent->modifiers;
+  event.mModifiers = aEvent->mModifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aDelta;
   event.inputSource = aEvent->inputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
                             &event, nullptr, &status);
@@ -2336,17 +2336,17 @@ EventStateManager::SendPixelScrollEvent(
   WidgetMouseScrollEvent event(aEvent->IsTrusted(),
                                eLegacyMousePixelScroll, aEvent->widget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.mTime = aEvent->mTime;
   event.mTimeStamp = aEvent->mTimeStamp;
-  event.modifiers = aEvent->modifiers;
+  event.mModifiers = aEvent->mModifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aPixelDelta;
   event.inputSource = aEvent->inputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
                             &event, nullptr, &status);
@@ -3344,23 +3344,23 @@ EventStateManager::PostHandleEvent(nsPre
       // dragover event and cancel the event. To not allow a drop somewhere,
       // don't cancel the event or set the effectAllowed or dropEffect to
       // "none". This way, if the event is just ignored, no drop will be
       // allowed.
       uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
       uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
       if (nsEventStatus_eConsumeNoDefault == *aStatus) {
         // if the event has a dataTransfer set, use it.
-        if (dragEvent->dataTransfer) {
+        if (dragEvent->mDataTransfer) {
           // get the dataTransfer and the dropEffect that was set on it
-          dataTransfer = do_QueryInterface(dragEvent->dataTransfer);
+          dataTransfer = do_QueryInterface(dragEvent->mDataTransfer);
           dataTransfer->GetDropEffectInt(&dropEffect);
         }
         else {
-          // if dragEvent->dataTransfer is null, it means that no attempt was
+          // if dragEvent->mDataTransfer is null, it means that no attempt was
           // made to access the dataTransfer during the event, yet the event
           // was cancelled. Instead, use the initial data transfer available
           // from the drag session. The drop effect would not have been
           // initialized (which is done in DragEvent::GetDataTransfer),
           // so set it from the drag action. We'll still want to filter it
           // based on the effectAllowed below.
           dataTransfer = initialDataTransfer;
 
@@ -3435,17 +3435,17 @@ EventStateManager::PostHandleEvent(nsPre
         WidgetDragEvent event(aEvent->IsTrusted(), eLegacyDragDrop, widget);
 
         WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
         event.refPoint = mouseEvent->refPoint;
         if (mouseEvent->widget) {
           event.refPoint += mouseEvent->widget->WidgetToScreenOffset();
         }
         event.refPoint -= widget->WidgetToScreenOffset();
-        event.modifiers = mouseEvent->modifiers;
+        event.mModifiers = mouseEvent->mModifiers;
         event.buttons = mouseEvent->buttons;
         event.inputSource = mouseEvent->inputSource;
 
         nsEventStatus status = nsEventStatus_eIgnore;
         nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
         if (presShell) {
           presShell->HandleEventWithTarget(&event, mCurrentTarget,
                                            targetContent, &status);
@@ -3872,17 +3872,17 @@ CreateMouseOrPointerWidgetEvent(WidgetMo
     aNewEvent = newPointerEvent.forget();
   } else {
     aNewEvent =
       new WidgetMouseEvent(aMouseEvent->IsTrusted(), aMessage,
                            aMouseEvent->widget, WidgetMouseEvent::eReal);
     aNewEvent->relatedTarget = aRelatedContent;
   }
   aNewEvent->refPoint = aMouseEvent->refPoint;
-  aNewEvent->modifiers = aMouseEvent->modifiers;
+  aNewEvent->mModifiers = aMouseEvent->mModifiers;
   aNewEvent->button = aMouseEvent->button;
   aNewEvent->buttons = aMouseEvent->buttons;
   aNewEvent->pressure = aMouseEvent->pressure;
   aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent;
   aNewEvent->inputSource = aMouseEvent->inputSource;
 }
 
 nsIFrame*
@@ -4481,17 +4481,17 @@ EventStateManager::FireDragEnterOrExit(n
                                        EventMessage aMessage,
                                        nsIContent* aRelatedTarget,
                                        nsIContent* aTargetContent,
                                        nsWeakFrame& aTargetFrame)
 {
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->widget);
   event.refPoint = aDragEvent->refPoint;
-  event.modifiers = aDragEvent->modifiers;
+  event.mModifiers = aDragEvent->mModifiers;
   event.buttons = aDragEvent->buttons;
   event.relatedTarget = aRelatedTarget;
   event.inputSource = aDragEvent->inputSource;
 
   mCurrentTargetContent = aTargetContent;
 
   if (aTargetContent != aRelatedTarget) {
     //XXX This event should still go somewhere!!
@@ -4518,30 +4518,31 @@ EventStateManager::FireDragEnterOrExit(n
   if (aTargetFrame)
     aTargetFrame->HandleEvent(aPresContext, &event, &status);
 }
 
 void
 EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent)
 {
   NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!");
-  if (!dragEvent->dataTransfer)
+  if (!dragEvent->mDataTransfer) {
     return;
+  }
 
   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
 
   if (dragSession) {
     // the initial dataTransfer is the one from the dragstart event that
     // was set on the dragSession when the drag began.
     nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
     dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
     if (initialDataTransfer) {
       // retrieve the current moz cursor setting and save it.
       nsAutoString mozCursor;
-      dragEvent->dataTransfer->GetMozCursor(mozCursor);
+      dragEvent->mDataTransfer->GetMozCursor(mozCursor);
       initialDataTransfer->SetMozCursor(mozCursor);
     }
   }
 }
 
 nsresult
 EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
                                  nsEventStatus* aStatus)
@@ -4637,17 +4638,17 @@ EventStateManager::CheckForAndDispatchCl
     bool notDispatchToContents =
      (aEvent->button == WidgetMouseEvent::eMiddleButton ||
       aEvent->button == WidgetMouseEvent::eRightButton);
 
     WidgetMouseEvent event(aEvent->IsTrusted(), eMouseClick,
                            aEvent->widget, WidgetMouseEvent::eReal);
     event.refPoint = aEvent->refPoint;
     event.clickCount = aEvent->clickCount;
-    event.modifiers = aEvent->modifiers;
+    event.mModifiers = aEvent->mModifiers;
     event.buttons = aEvent->buttons;
     event.mTime = aEvent->mTime;
     event.mTimeStamp = aEvent->mTimeStamp;
     event.mFlags.mNoContentDispatch = notDispatchToContents;
     event.button = aEvent->button;
     event.inputSource = aEvent->inputSource;
 
     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
@@ -4671,17 +4672,17 @@ EventStateManager::CheckForAndDispatchCl
                                              mouseContent, aStatus);
       if (NS_SUCCEEDED(ret) && aEvent->clickCount == 2 &&
           mouseContent && mouseContent->IsInComposedDoc()) {
         //fire double click
         WidgetMouseEvent event2(aEvent->IsTrusted(), eMouseDoubleClick,
                                 aEvent->widget, WidgetMouseEvent::eReal);
         event2.refPoint = aEvent->refPoint;
         event2.clickCount = aEvent->clickCount;
-        event2.modifiers = aEvent->modifiers;
+        event2.mModifiers = aEvent->mModifiers;
         event2.buttons = aEvent->buttons;
         event2.mFlags.mNoContentDispatch = notDispatchToContents;
         event2.button = aEvent->button;
         event2.inputSource = aEvent->inputSource;
 
         ret = presShell->HandleEventWithTarget(&event2, currentTarget,
                                                mouseContent, aStatus);
       }
@@ -5522,21 +5523,21 @@ EventStateManager::WheelPrefs::Reset()
 EventStateManager::WheelPrefs::Index
 EventStateManager::WheelPrefs::GetIndexFor(WidgetWheelEvent* aEvent)
 {
   if (!aEvent) {
     return INDEX_DEFAULT;
   }
 
   Modifiers modifiers =
-    (aEvent->modifiers & (MODIFIER_ALT |
-                          MODIFIER_CONTROL |
-                          MODIFIER_META |
-                          MODIFIER_SHIFT |
-                          MODIFIER_OS));
+    (aEvent->mModifiers & (MODIFIER_ALT |
+                           MODIFIER_CONTROL |
+                           MODIFIER_META |
+                           MODIFIER_SHIFT |
+                           MODIFIER_OS));
 
   switch (modifiers) {
     case MODIFIER_ALT:
       return INDEX_ALT;
     case MODIFIER_CONTROL:
       return INDEX_CONTROL;
     case MODIFIER_META:
       return INDEX_META;
--- a/dom/events/EventTarget.cpp
+++ b/dom/events/EventTarget.cpp
@@ -35,17 +35,17 @@ EventTarget::SetEventHandler(const nsASt
                              EventHandlerNonNull* aHandler,
                              ErrorResult& aRv)
 {
   if (!StringBeginsWith(aType, NS_LITERAL_STRING("on"))) {
     aRv.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
   if (NS_IsMainThread()) {
-    nsCOMPtr<nsIAtom> type = do_GetAtom(aType);
+    nsCOMPtr<nsIAtom> type = NS_Atomize(aType);
     SetEventHandler(type, EmptyString(), aHandler);
     return;
   }
   SetEventHandler(nullptr,
                   Substring(aType, 2), // Remove "on"
                   aHandler);
 }
 
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -50,17 +50,17 @@ public:
                                    EventListener* aCallback,
                                    bool aCapture,
                                    ErrorResult& aRv);
   bool DispatchEvent(Event& aEvent, ErrorResult& aRv);
 
   // Note, this takes the type in onfoo form!
   EventHandlerNonNull* GetEventHandler(const nsAString& aType)
   {
-    nsCOMPtr<nsIAtom> type = do_GetAtom(aType);
+    nsCOMPtr<nsIAtom> type = NS_Atomize(aType);
     return GetEventHandler(type, EmptyString());
   }
 
   // Note, this takes the type in onfoo form!
   void SetEventHandler(const nsAString& aType, EventHandlerNonNull* aHandler,
                        ErrorResult& rv);
 
   // Note, for an event 'foo' aType will be 'onfoo'.
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -810,17 +810,17 @@ IMEContentObserver::OnMouseButtonEvent(n
   notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
   notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
   notification.mMouseButtonEventData.mCursorPos.Set(
     charAtPt.refPoint.ToUnknownPoint());
   notification.mMouseButtonEventData.mCharRect.Set(
     charAtPt.mReply.mRect.ToUnknownRect());
   notification.mMouseButtonEventData.mButton = aMouseEvent->button;
   notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
-  notification.mMouseButtonEventData.mModifiers = aMouseEvent->modifiers;
+  notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers;
 
   nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
   if (consumed) {
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -150,17 +150,17 @@ MouseEvent::InitMouseEvent(const nsAStri
 
   switch(mEvent->mClass) {
     case eMouseEventClass:
     case eMouseScrollEventClass:
     case eWheelEventClass:
     case eDragEventClass:
     case ePointerEventClass:
     case eSimpleGestureEventClass:
-      mEvent->AsInputEvent()->modifiers = modifiers;
+      mEvent->AsInputEvent()->mModifiers = modifiers;
       return;
     default:
       MOZ_CRASH("There is no space to store the modifiers");
   }
 }
 
 void
 MouseEvent::InitializeExtraMouseEventDictionaryMembers(const MouseEventInit& aParam)
--- a/dom/events/MutationEvent.cpp
+++ b/dom/events/MutationEvent.cpp
@@ -96,21 +96,21 @@ MutationEvent::InitMutationEvent(const n
                                  const nsAString& aAttrNameArg,
                                  uint16_t aAttrChangeArg)
 {
   Event::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
 
   InternalMutationEvent* mutation = mEvent->AsMutationEvent();
   mutation->mRelatedNode = aRelatedNodeArg;
   if (!aPrevValueArg.IsEmpty())
-    mutation->mPrevAttrValue = do_GetAtom(aPrevValueArg);
+    mutation->mPrevAttrValue = NS_Atomize(aPrevValueArg);
   if (!aNewValueArg.IsEmpty())
-    mutation->mNewAttrValue = do_GetAtom(aNewValueArg);
+    mutation->mNewAttrValue = NS_Atomize(aNewValueArg);
   if (!aAttrNameArg.IsEmpty()) {
-    mutation->mAttrName = do_GetAtom(aAttrNameArg);
+    mutation->mAttrName = NS_Atomize(aAttrNameArg);
   }
   mutation->mAttrChange = aAttrChangeArg;
     
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -52,17 +52,17 @@ UIEvent::UIEvent(EventTarget* aOwner,
     {
       mDetail = mEvent->AsUIEvent()->mDetail;
       break;
     }
 
     case eScrollPortEventClass:
     {
       InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent();
-      mDetail = (int32_t)scrollEvent->orient;
+      mDetail = static_cast<int32_t>(scrollEvent->mOrient);
       break;
     }
 
     default:
       mDetail = 0;
       break;
   }
 
@@ -463,37 +463,37 @@ UIEvent::ComputeModifierState(const nsAS
   return modifiers;
 }
 
 bool
 UIEvent::GetModifierStateInternal(const nsAString& aKey)
 {
   WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
   MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class");
-  return ((inputEvent->modifiers & WidgetInputEvent::GetModifier(aKey)) != 0);
+  return ((inputEvent->mModifiers & WidgetInputEvent::GetModifier(aKey)) != 0);
 }
 
 void
 UIEvent::InitModifiers(const EventModifierInit& aParam)
 {
   if (NS_WARN_IF(!mEvent)) {
     return;
   }
   WidgetInputEvent* inputEvent = mEvent->AsInputEvent();
   MOZ_ASSERT(inputEvent,
              "This method shouldn't be called if it doesn't have modifiers");
   if (NS_WARN_IF(!inputEvent)) {
     return;
   }
 
-  inputEvent->modifiers = MODIFIER_NONE;
+  inputEvent->mModifiers = MODIFIER_NONE;
 
 #define SET_MODIFIER(aName, aValue) \
   if (aParam.m##aName) { \
-    inputEvent->modifiers |= aValue; \
+    inputEvent->mModifiers |= aValue; \
   } \
 
   SET_MODIFIER(CtrlKey,                 MODIFIER_CONTROL)
   SET_MODIFIER(ShiftKey,                MODIFIER_SHIFT)
   SET_MODIFIER(AltKey,                  MODIFIER_ALT)
   SET_MODIFIER(MetaKey,                 MODIFIER_META)
   SET_MODIFIER(ModifierAltGraph,        MODIFIER_ALTGRAPH)
   SET_MODIFIER(ModifierCapsLock,        MODIFIER_CAPSLOCK)
--- a/dom/html/HTMLAllCollection.cpp
+++ b/dom/html/HTMLAllCollection.cpp
@@ -109,17 +109,17 @@ DocAllResultMatch(nsIContent* aContent, 
 
 nsContentList*
 HTMLAllCollection::GetDocumentAllList(const nsAString& aID)
 {
   if (nsContentList* docAllList = mNamedMap.GetWeak(aID)) {
     return docAllList;
   }
 
-  nsCOMPtr<nsIAtom> id = do_GetAtom(aID);
+  nsCOMPtr<nsIAtom> id = NS_Atomize(aID);
   RefPtr<nsContentList> docAllList =
     new nsContentList(mDocument, DocAllResultMatch, nullptr, nullptr, true, id);
   mNamedMap.Put(aID, docAllList);
   return docAllList;
 }
 
 void
 HTMLAllCollection::NamedGetter(const nsAString& aID,
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3099,21 +3099,23 @@ public:
                                 TrackID aInputTrackID) override
   {
     if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
       return;
     }
     const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
     for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
       if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
+        mInitialSizeFound = true;
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethodWithArgs<gfx::IntSize>(
               this, &StreamSizeListener::ReceivedSize,
               c->mFrame.GetIntrinsicSize());
         aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+        return;
       }
     }
   }
 
 private:
   // These fields may only be accessed on the main thread
   HTMLMediaElement* mElement;
 
--- a/dom/html/nsDOMStringMap.cpp
+++ b/dom/html/nsDOMStringMap.cpp
@@ -107,17 +107,17 @@ nsDOMStringMap::NamedSetter(const nsAStr
   }
 
   nsresult res = nsContentUtils::CheckQName(attr, false);
   if (NS_FAILED(res)) {
     rv.Throw(res);
     return;
   }
 
-  nsCOMPtr<nsIAtom> attrAtom = do_GetAtom(attr);
+  nsCOMPtr<nsIAtom> attrAtom = NS_Atomize(attr);
   MOZ_ASSERT(attrAtom, "Should be infallible");
 
   res = mElement->SetAttr(kNameSpaceID_None, attrAtom, aValue, true);
   if (NS_FAILED(res)) {
     rv.Throw(res);
   }
 }
 
@@ -131,17 +131,17 @@ nsDOMStringMap::NamedDeleter(const nsASt
   }
   
   nsAutoString attr;
   if (!DataPropToAttr(aProp, attr)) {
     found = false;
     return;
   }
 
-  nsCOMPtr<nsIAtom> attrAtom = do_GetAtom(attr);
+  nsCOMPtr<nsIAtom> attrAtom = NS_Atomize(attr);
   MOZ_ASSERT(attrAtom, "Should be infallible");
 
   found = mElement->HasAttr(kNameSpaceID_None, attrAtom);
 
   if (found) {
     mRemovingProp = true;
     mElement->UnsetAttr(kNameSpaceID_None, attrAtom, true);
     mRemovingProp = false;
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2421,17 +2421,17 @@ nsGenericHTMLFormElement::AddFormIdObser
   NS_ASSERTION(GetUncomposedDoc(), "When adding a form id observer, "
                                    "we should be in a document!");
 
   nsAutoString formId;
   nsIDocument* doc = OwnerDoc();
   GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
   NS_ASSERTION(!formId.IsEmpty(),
                "@form value should not be the empty string!");
-  nsCOMPtr<nsIAtom> atom = do_GetAtom(formId);
+  nsCOMPtr<nsIAtom> atom = NS_Atomize(formId);
 
   return doc->AddIDTargetObserver(atom, FormIdUpdated, this, false);
 }
 
 void
 nsGenericHTMLFormElement::RemoveFormIdObserver()
 {
   /**
@@ -2450,17 +2450,17 @@ nsGenericHTMLFormElement::RemoveFormIdOb
   if (!doc) {
     return;
   }
 
   nsAutoString formId;
   GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
   NS_ASSERTION(!formId.IsEmpty(),
                "@form value should not be the empty string!");
-  nsCOMPtr<nsIAtom> atom = do_GetAtom(formId);
+  nsCOMPtr<nsIAtom> atom = NS_Atomize(formId);
 
   doc->RemoveIDTargetObserver(atom, FormIdUpdated, this, false);
 }
 
 
 /* static */
 bool
 nsGenericHTMLFormElement::FormIdUpdated(Element* aOldElement,
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGenericHTMLFrameElement.h"
 
 #include "mozilla/dom/BrowserElementAudioChannel.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ErrorResult.h"
 #include "GeckoProfiler.h"
 #include "mozIApplication.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentUtils.h"
 #include "nsIAppsService.h"
 #include "nsIDocShell.h"
@@ -20,16 +21,17 @@
 #include "nsIFrame.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsIPresShell.h"
 #include "nsIScrollable.h"
 #include "nsPresContext.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSubDocumentFrame.h"
+#include "nsXULElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                   nsGenericHTMLElement)
@@ -206,21 +208,47 @@ nsGenericHTMLFrameElement::GetParentAppl
   rv = appsService->GetAppByLocalId(appId, aApplication);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
+void
+nsGenericHTMLFrameElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
+                                            ErrorResult& rv)
+{
+  if (&aOtherLoaderOwner == this) {
+    // nothing to do
+    return;
+  }
+
+  SwapFrameLoaders(aOtherLoaderOwner.mFrameLoader, rv);
+}
+
+void
+nsGenericHTMLFrameElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
+                                            ErrorResult& rv)
 {
-  // We don't support this yet
-  return NS_ERROR_NOT_IMPLEMENTED;
+  aOtherLoaderOwner.SwapFrameLoaders(mFrameLoader, rv);
+}
+
+void
+nsGenericHTMLFrameElement::SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+                                            mozilla::ErrorResult& rv)
+{
+  if (!mFrameLoader || !aOtherLoader) {
+    rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return;
+  }
+
+  rv = mFrameLoader->SwapWithOtherLoader(aOtherLoader,
+                                         mFrameLoader,
+                                         aOtherLoader);
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SetIsPrerendered()
 {
   MOZ_ASSERT(!mFrameLoader, "Please call SetIsPrerendered before frameLoader is created");
   mIsPrerendered = true;
   return NS_OK;
@@ -707,15 +735,8 @@ nsGenericHTMLFrameElement::AllowCreateFr
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::InitializeBrowserAPI()
 {
   MOZ_ASSERT(mFrameLoader);
   InitBrowserElementAPI();
   return NS_OK;
 }
-
-void
-nsGenericHTMLFrameElement::SwapFrameLoaders(nsXULElement& aOtherOwner,
-                                            ErrorResult& aError)
-{
-  aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
-}
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -69,17 +69,24 @@ public:
 
   nsresult CopyInnerTo(mozilla::dom::Element* aDest);
 
   virtual int32_t TabIndexDefault() override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGenericHTMLFrameElement,
                                            nsGenericHTMLElement)
 
-  void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aError);
+  void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
+                        mozilla::ErrorResult& aError);
+
+  void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
+                        mozilla::ErrorResult& aError);
+
+  void SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+                        mozilla::ErrorResult& rv);
 
   static bool BrowserFramesEnabled();
 
   /**
    * Helper method to map a HTML 'scrolling' attribute value to a nsIScrollable
    * enum value.  scrolling="no" (and its synonyms) maps to
    * nsIScrollable::Scrollbar_Never, and anything else (including nullptr) maps
    * to nsIScrollable::Scrollbar_Auto.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2026,17 +2026,17 @@ static void* CreateTokens(nsINode* aRoot
   // parse the tokens
   while (iter != end) {
     nsAString::const_iterator start(iter);
 
     do {
       ++iter;
     } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
 
-    tokens->AppendElement(do_GetAtom(Substring(start, iter)));
+    tokens->AppendElement(NS_Atomize(Substring(start, iter)));
 
     // skip whitespace
     while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
       ++iter;
     }
   }
   return tokens;
 }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2083,17 +2083,17 @@ IDBObjectStore::Count(JSContext* aCx,
 already_AddRefed<IDBRequest>
 IDBObjectStore::OpenCursorInternal(bool aKeysOnly,
                                    JSContext* aCx,
                                    JS::Handle<JS::Value> aRange,
                                    IDBCursorDirection aDirection,
                                    ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT_IF(!aCx, aRange.isUndefined());
+  MOZ_ASSERT(aCx);
 
   if (mDeletedSpec) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -257,22 +257,23 @@ public:
   {
     AssertIsOnOwningThread();
 
     return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection,
                               aRv);
   }
 
   already_AddRefed<IDBRequest>
-  OpenCursor(IDBCursorDirection aDirection,
+  OpenCursor(JSContext* aCx,
+             IDBCursorDirection aDirection,
              ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
 
-    return OpenCursorInternal(/* aKeysOnly */ false, nullptr,
+    return OpenCursorInternal(/* aKeysOnly */ false, aCx,
                               JS::UndefinedHandleValue, aDirection, aRv);
   }
 
   already_AddRefed<IDBRequest>
   OpenKeyCursor(JSContext* aCx,
                 JS::Handle<JS::Value> aRange,
                 IDBCursorDirection aDirection,
                 ErrorResult& aRv)
@@ -342,18 +343,16 @@ private:
                  ErrorResult& aRv);
 
   already_AddRefed<IDBIndex>
   CreateIndexInternal(const nsAString& aName,
                       const KeyPath& aKeyPath,
                       const IDBIndexParameters& aOptionalParameters,
                       ErrorResult& aRv);
 
-  // aCx is allowed to be null but only if aRange.isUndefined().  In that case,
-  // we don't actually use aCx for anything, so it's OK.
   already_AddRefed<IDBRequest>
   OpenCursorInternal(bool aKeysOnly,
                      JSContext* aCx,
                      JS::Handle<JS::Value> aRange,
                      IDBCursorDirection aDirection,
                      ErrorResult& aRv);
 };
 
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -111,16 +111,17 @@ IDBRequest::InitMembers()
 }
 
 // static
 already_AddRefed<IDBRequest>
 IDBRequest::Create(JSContext* aCx,
                    IDBDatabase* aDatabase,
                    IDBTransaction* aTransaction)
 {
+  MOZ_ASSERT(aCx);
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
 
   RefPtr<IDBRequest> request = new IDBRequest(aDatabase);
   CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
 
   request->mTransaction = aTransaction;
   request->SetScriptOwner(aDatabase->GetScriptOwner());
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2759,17 +2759,17 @@ TabParent::InjectTouchEvent(const nsAStr
   }
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   WidgetTouchEvent event(true, msg, widget);
-  event.modifiers = aModifiers;
+  event.mModifiers = aModifiers;
   event.mTime = PR_IntervalNow();
 
   nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
   if (!content || !content->OwnerDoc()) {
     return NS_ERROR_FAILURE;
   }
 
   nsIDocument* doc = content->OwnerDoc();
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -334,20 +334,18 @@ AudioStream::Init(uint32_t aNumChannels,
 
   mDumpFile = OpenDumpFile(this);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
-  mAudioChannel = aAudioChannel;
   params.stream_type = CubebUtils::ConvertChannelToCubebType(aAudioChannel);
 #else
-  mAudioChannel = dom::AudioChannel::Content;
   params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
 #endif
 
   if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 #endif
 
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -338,19 +338,16 @@ private:
   Monitor mMonitor;
 
   // Input rate in Hz (characteristic of the media being played)
   uint32_t mInRate;
   // Output rate in Hz (characteristic of the playback rate)
   uint32_t mOutRate;
   uint32_t mChannels;
   uint32_t mOutChannels;
-#if defined(__ANDROID__)
-  dom::AudioChannel mAudioChannel;
-#endif
   AudioClock mAudioClock;
   soundtouch::SoundTouch* mTimeStretcher;
 
   // Stream start time for stream open delay telemetry.
   TimeStamp mStartTime;
 
   // Output file for dumping audio
   FILE* mDumpFile;
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -614,50 +614,53 @@ AudioCallbackDriver::Init()
 
   input = output;
   input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream = nullptr;
   CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
   // We have to translate the deviceID values to cubeb devid's since those can be
   // freed whenever enumerate is called.
-  if ((!mGraphImpl->mInputWanted
+  {
+    StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
+    if ((!mGraphImpl->mInputWanted
 #ifdef MOZ_WEBRTC
-       || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id)
+         || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id)
 #endif
-       ) &&
-      (mGraphImpl->mOutputDeviceID == -1 // pass nullptr for ID for default output
+         ) &&
+        (mGraphImpl->mOutputDeviceID == -1 // pass nullptr for ID for default output
 #ifdef MOZ_WEBRTC
-       // XXX we should figure out how we would use a deviceID for output without webrtc.
-       // Currently we don't set this though, so it's ok
-       || AudioInputCubeb::GetDeviceID(mGraphImpl->mOutputDeviceID, output_id)
+         // XXX we should figure out how we would use a deviceID for output without webrtc.
+         // Currently we don't set this though, so it's ok
+         || AudioInputCubeb::GetDeviceID(mGraphImpl->mOutputDeviceID, output_id)
 #endif
-       ) &&
-      // XXX Only pass input input if we have an input listener.  Always
-      // set up output because it's easier, and it will just get silence.
-      // XXX Add support for adding/removing an input listener later.
-      cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
-                        "AudioCallbackDriver",
-                        input_id,
-                        mGraphImpl->mInputWanted ? &input : nullptr,
-                        output_id,
-                        mGraphImpl->mOutputWanted ? &output : nullptr, latency,
-                        DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
-    mAudioStream.own(stream);
-  } else {
-    NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
-    // Fall back to a driver using a normal thread.
-    MonitorAutoLock lock(GraphImpl()->GetMonitor());
-    SetNextDriver(new SystemClockDriver(GraphImpl()));
-    NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
-    mGraphImpl->SetCurrentDriver(NextDriver());
-    NextDriver()->Start();
-    return;
+         ) &&
+        // XXX Only pass input input if we have an input listener.  Always
+        // set up output because it's easier, and it will just get silence.
+        // XXX Add support for adding/removing an input listener later.
+        cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
+                          "AudioCallbackDriver",
+                          input_id,
+                          mGraphImpl->mInputWanted ? &input : nullptr,
+                          output_id,
+                          mGraphImpl->mOutputWanted ? &output : nullptr, latency,
+                          DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
+      mAudioStream.own(stream);
+    } else {
+      StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex());
+      NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
+      // Fall back to a driver using a normal thread.
+      MonitorAutoLock lock(GraphImpl()->GetMonitor());
+      SetNextDriver(new SystemClockDriver(GraphImpl()));
+      NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
+      mGraphImpl->SetCurrentDriver(NextDriver());
+      NextDriver()->Start();
+      return;
+    }
   }
-
   cubeb_stream_register_device_changed_callback(mAudioStream,
                                                 AudioCallbackDriver::DeviceChangedCallback_s);
 
   StartStream();
 
   STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver started."));
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/MozPromise.h"
+#include "MediaDecoderReaderWrapper.h"
+
+namespace mozilla {
+
+extern LazyLogModule gMediaDecoderLog;
+
+#undef LOG
+#define LOG(...) \
+  MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+
+// StartTimeRendezvous is a helper class that quarantines the first sample
+// until it gets a sample from both channels, such that we can be guaranteed
+// to know the start time by the time On{Audio,Video}Decoded is called on MDSM.
+class StartTimeRendezvous {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous);
+  typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
+  typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
+
+public:
+  StartTimeRendezvous(AbstractThread* aOwnerThread,
+                      bool aHasAudio,
+                      bool aHasVideo,
+                      bool aForceZeroStartTime)
+    : mOwnerThread(aOwnerThread)
+  {
+    if (aForceZeroStartTime) {
+      mAudioStartTime.emplace(0);
+      mVideoStartTime.emplace(0);
+      return;
+    }
+    if (!aHasAudio) {
+      mAudioStartTime.emplace(INT64_MAX);
+    }
+    if (!aHasVideo) {
+      mVideoStartTime.emplace(INT64_MAX);
+    }
+  }
+
+  void Destroy()
+  {
+    mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX));
+    mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX));
+    mHaveStartTimePromise.RejectIfExists(false, __func__);
+  }
+
+  RefPtr<HaveStartTimePromise> AwaitStartTime()
+  {
+    if (HaveStartTime()) {
+      return HaveStartTimePromise::CreateAndResolve(true, __func__);
+    }
+    return mHaveStartTimePromise.Ensure(__func__);
+  }
+
+  template<typename PromiseType>
+  struct PromiseSampleType {
+    typedef typename PromiseType::ResolveValueType::element_type Type;
+  };
+
+  template<typename PromiseType, MediaData::Type SampleType>
+  RefPtr<PromiseType>
+  ProcessFirstSample(typename PromiseSampleType<PromiseType>::Type* aData)
+  {
+    typedef typename PromiseSampleType<PromiseType>::Type DataType;
+    typedef typename PromiseType::Private PromisePrivate;
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+
+    MaybeSetChannelStartTime<SampleType>(aData->mTime);
+
+    RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
+    RefPtr<DataType> data = aData;
+    RefPtr<StartTimeRendezvous> self = this;
+    AwaitStartTime()->Then(
+      mOwnerThread, __func__,
+      [p, data, self] () {
+        MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn());
+        p->Resolve(data, __func__);
+      },
+      [p] () {
+        p->Reject(MediaDecoderReader::CANCELED, __func__);
+      });
+
+    return p.forget();
+  }
+
+  template<MediaData::Type SampleType>
+  void FirstSampleRejected(MediaDecoderReader::NotDecodedReason aReason)
+  {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+    if (aReason == MediaDecoderReader::DECODE_ERROR) {
+      mHaveStartTimePromise.RejectIfExists(false, __func__);
+    } else if (aReason == MediaDecoderReader::END_OF_STREAM) {
+      LOG("StartTimeRendezvous=%p SampleType(%d) Has no samples.",
+           this, SampleType);
+      MaybeSetChannelStartTime<SampleType>(INT64_MAX);
+    }
+  }
+
+  bool HaveStartTime() const
+  {
+    return mAudioStartTime.isSome() && mVideoStartTime.isSome();
+  }
+
+  int64_t StartTime() const
+  {
+    int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref());
+    return time == INT64_MAX ? 0 : time;
+  }
+
+private:
+  ~StartTimeRendezvous() {}
+
+  template<MediaData::Type SampleType>
+  void MaybeSetChannelStartTime(int64_t aStartTime)
+  {
+    if (ChannelStartTime(SampleType).isSome()) {
+      // If we're initialized with aForceZeroStartTime=true, the channel start
+      // times are already set.
+      return;
+    }
+
+    LOG("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld",
+        this, SampleType, aStartTime);
+
+    ChannelStartTime(SampleType).emplace(aStartTime);
+    if (HaveStartTime()) {
+      mHaveStartTimePromise.ResolveIfExists(true, __func__);
+    }
+  }
+
+  Maybe<int64_t>& ChannelStartTime(MediaData::Type aType)
+  {
+    return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime;
+  }
+
+  MozPromiseHolder<HaveStartTimePromise> mHaveStartTimePromise;
+  RefPtr<AbstractThread> mOwnerThread;
+  Maybe<int64_t> mAudioStartTime;
+  Maybe<int64_t> mVideoStartTime;
+};
+
+MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(bool aIsRealTime,
+                                                     AbstractThread* aOwnerThread,
+                                                     MediaDecoderReader* aReader)
+  : mForceZeroStartTime(aIsRealTime || aReader->ForceZeroStartTime())
+  , mOwnerThread(aOwnerThread)
+  , mReader(aReader)
+{}
+
+MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper()
+{}
+
+media::TimeUnit
+MediaDecoderReaderWrapper::StartTime() const
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_ASSERT(!mShutdown);
+  return media::TimeUnit::FromMicroseconds(mStartTimeRendezvous->StartTime());
+}
+
+RefPtr<MediaDecoderReaderWrapper::MetadataPromise>
+MediaDecoderReaderWrapper::ReadMetadata()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_ASSERT(!mShutdown);
+  return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
+                     &MediaDecoderReader::AsyncReadMetadata)
+         ->Then(mOwnerThread, __func__, this,
+                &MediaDecoderReaderWrapper::OnMetadataRead,
+                &MediaDecoderReaderWrapper::OnMetadataNotRead)
+         ->CompletionPromise();
+}
+
+RefPtr<HaveStartTimePromise>
+MediaDecoderReaderWrapper::AwaitStartTime()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_ASSERT(!mShutdown);
+  return mStartTimeRendezvous->AwaitStartTime();
+}
+
+RefPtr<MediaDecoderReaderWrapper::AudioDataPromise>
+MediaDecoderReaderWrapper::RequestAudioData()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_ASSERT(!mShutdown);
+
+  auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
+                       &MediaDecoderReader::RequestAudioData);
+
+  if (!mStartTimeRendezvous->HaveStartTime()) {
+    p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
+                &StartTimeRendezvous::ProcessFirstSample<AudioDataPromise, MediaData::AUDIO_DATA>,
+                &StartTimeRendezvous::FirstSampleRejected<MediaData::AUDIO_DATA>)
+         ->CompletionPromise();
+  }
+
+  return p->Then(mOwnerThread, __func__, this,
+                 &MediaDecoderReaderWrapper::OnSampleDecoded,
+                 &MediaDecoderReaderWrapper::OnNotDecoded)
+          ->CompletionPromise();
+}
+
+RefPtr<MediaDecoderReaderWrapper::VideoDataPromise>
+MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
+                                            media::TimeUnit aTimeThreshold)
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_ASSERT(!mShutdown);
+
+  if (aTimeThreshold.ToMicroseconds() > 0 &&
+      mStartTimeRendezvous->HaveStartTime()) {
+    aTimeThreshold += StartTime();
+  }
+
+  auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
+                       &MediaDecoderReader::RequestVideoData,
+                       aSkipToNextKeyframe, aTimeThreshold.ToMicroseconds());
+
+  if (!mStartTimeRendezvous->HaveStartTime()) {
+    p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
+                &StartTimeRendezvous::ProcessFirstSample<VideoDataPromise, MediaData::VIDEO_DATA>,
+                &StartTimeRendezvous::FirstSampleRejected<MediaData::VIDEO_DATA>)
+         ->CompletionPromise();
+  }
+
+  return p->Then(mOwnerThread, __func__, this,
+                 &MediaDecoderReaderWrapper::OnSampleDecoded,
+                 &MediaDecoderReaderWrapper::OnNotDecoded)
+          ->CompletionPromise();
+}
+
+RefPtr<MediaDecoderReader::SeekPromise>
+MediaDecoderReaderWrapper::Seek(SeekTarget aTarget, media::TimeUnit aEndTime)
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  aTarget.SetTime(aTarget.GetTime() + StartTime());
+  return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
+                     &MediaDecoderReader::Seek, aTarget,
+                     aEndTime.ToMicroseconds());
+}
+
+void
+MediaDecoderReaderWrapper::Shutdown()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  mShutdown = true;
+  if (mStartTimeRendezvous) {
+    mStartTimeRendezvous->Destroy();
+    mStartTimeRendezvous = nullptr;
+  }
+}
+
+void
+MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  if (mShutdown) {
+    return;
+  }
+  // Set up the start time rendezvous if it doesn't already exist (which is
+  // generally the case, unless we're coming out of dormant mode).
+  if (!mStartTimeRendezvous) {
+    mStartTimeRendezvous = new StartTimeRendezvous(
+      mOwnerThread, aMetadata->mInfo.HasAudio(),
+      aMetadata->mInfo.HasVideo(), mForceZeroStartTime);
+
+    RefPtr<MediaDecoderReaderWrapper> self = this;
+    mStartTimeRendezvous->AwaitStartTime()->Then(
+      mOwnerThread, __func__,
+      [self] ()  {
+        NS_ENSURE_TRUE_VOID(!self->mShutdown);
+        self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds());
+      },
+      [] () {
+        NS_WARNING("Setting start time on reader failed");
+      });
+  }
+}
+
+void
+MediaDecoderReaderWrapper::OnSampleDecoded(MediaData* aSample)
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  if (!mShutdown) {
+    aSample->AdjustForStartTime(StartTime().ToMicroseconds());
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MediaDecoderReaderWrapper_h_
+#define MediaDecoderReaderWrapper_h_
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+
+#include "MediaDecoderReader.h"
+
+namespace mozilla {
+
+class StartTimeRendezvous;
+
+typedef MozPromise<bool, bool, /* isExclusive = */ false> HaveStartTimePromise;
+
+/**
+ * A wrapper around MediaDecoderReader to offset the timestamps of Audio/Video
+ * samples by the start time to ensure MDSM can always assume zero start time.
+ * It also adjusts the seek target passed to Seek() to ensure correct seek time
+ * is passed to the underlying reader.
+ */
+class MediaDecoderReaderWrapper {
+  typedef MediaDecoderReader::MetadataPromise MetadataPromise;
+  typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
+  typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
+  typedef MediaDecoderReader::SeekPromise SeekPromise;
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper);
+
+public:
+  MediaDecoderReaderWrapper(bool aIsRealTime,
+                            AbstractThread* aOwnerThread,
+                            MediaDecoderReader* aReader);
+
+  media::TimeUnit StartTime() const;
+  RefPtr<MetadataPromise> ReadMetadata();
+  RefPtr<HaveStartTimePromise> AwaitStartTime();
+  RefPtr<AudioDataPromise> RequestAudioData();
+  RefPtr<VideoDataPromise> RequestVideoData(bool aSkipToNextKeyframe,
+                                            media::TimeUnit aTimeThreshold);
+  RefPtr<SeekPromise> Seek(SeekTarget aTarget, media::TimeUnit aEndTime);
+  void Shutdown();
+
+private:
+  ~MediaDecoderReaderWrapper();
+
+  void OnMetadataRead(MetadataHolder* aMetadata);
+  void OnMetadataNotRead() {}
+  void OnSampleDecoded(MediaData* aSample);
+  void OnNotDecoded() {}
+
+  const bool mForceZeroStartTime;
+  const RefPtr<AbstractThread> mOwnerThread;
+  const RefPtr<MediaDecoderReader> mReader;
+
+  bool mShutdown = false;
+  RefPtr<StartTimeRendezvous> mStartTimeRendezvous;
+};
+
+} // namespace mozilla
+
+#endif // MediaDecoderReaderWrapper_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -37,16 +37,17 @@
 #include "nsDeque.h"
 #include "prenv.h"
 
 #include "AudioSegment.h"
 #include "DOMMediaStream.h"
 #include "ImageContainer.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
+#include "MediaDecoderReaderWrapper.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaShutdownManager.h"
 #include "MediaTimer.h"
 #include "TimeUnits.h"
 #include "VideoSegment.h"
 #include "VideoUtils.h"
 #include "gfxPrefs.h"
 
@@ -214,16 +215,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   mRealTime(aRealTime),
   mDispatchedStateMachine(false),
   mDelayedScheduler(mTaskQueue),
   mState(DECODER_STATE_DECODING_METADATA, "MediaDecoderStateMachine::mState"),
   mCurrentFrameID(0),
   mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"),
   mFragmentEndTime(-1),
   mReader(aReader),
+  mReaderWrapper(new MediaDecoderReaderWrapper(aRealTime, mTaskQueue, aReader)),
   mDecodedAudioEndTime(0),
   mDecodedVideoEndTime(0),
   mPlaybackRate(1.0),
   mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(detail::AMPLE_AUDIO_USECS),
   mQuickBufferingLowDataThresholdUsecs(detail::QUICK_BUFFERING_LOW_DATA_USECS),
   mIsAudioPrerolling(false),
   mIsVideoPrerolling(false),
@@ -599,17 +601,16 @@ MediaDecoderStateMachine::IsVideoSeekCom
 
 void
 MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   RefPtr<MediaData> audio(aAudioSample);
   MOZ_ASSERT(audio);
   mAudioDataRequest.Complete();
-  aAudioSample->AdjustForStartTime(StartTime());
 
   // audio->GetEndTime() is not always mono-increasing in chained ogg.
   mDecodedAudioEndTime = std::max(audio->GetEndTime(), mDecodedAudioEndTime);
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
              (audio ? audio->mTime : -1),
              (audio ? audio->GetEndTime() : -1),
              (audio ? audio->mDiscontinuity : 0));
@@ -882,17 +883,16 @@ MediaDecoderStateMachine::MaybeFinishDec
 void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   RefPtr<MediaData> video(aVideoSample);
   MOZ_ASSERT(video);
   mVideoDataRequest.Complete();
-  aVideoSample->AdjustForStartTime(StartTime());
 
   // Handle abnormal or negative timestamps.
   mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, video->GetEndTime());
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
              (video ? video->GetEndTime() : -1),
              (video ? video->mDiscontinuity : 0));
@@ -1337,21 +1337,17 @@ MediaDecoderStateMachine::Shutdown()
 
   if (IsPlaying()) {
     StopPlayback();
   }
 
   Reset();
 
   mMediaSink->Shutdown();
-
-  // Shut down our start time rendezvous.
-  if (mStartTimeRendezvous) {
-    mStartTimeRendezvous->Destroy();
-  }
+  mReaderWrapper->Shutdown();
 
   DECODER_LOG("Shutdown started");
 
   // Put a task in the decode queue to shutdown the reader.
   // the queue to spin down.
   return InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
                      &MediaDecoderReader::Shutdown)
     ->Then(OwnerThread(), __func__, this,
@@ -1478,18 +1474,17 @@ MediaDecoderStateMachine::ReadMetadata()
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(!IsShutdown());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   MOZ_ASSERT(!mMetadataRequest.Exists());
 
   DECODER_LOG("Dispatching AsyncReadMetadata");
   // Set mode to METADATA since we are about to read metadata.
   mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
-  mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
-                                     &MediaDecoderReader::AsyncReadMetadata)
+  mMetadataRequest.Begin(mReaderWrapper->ReadMetadata()
     ->Then(OwnerThread(), __func__, this,
            &MediaDecoderStateMachine::OnMetadataRead,
            &MediaDecoderStateMachine::OnMetadataNotRead));
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::Seek(SeekTarget aTarget)
 {
@@ -1632,21 +1627,18 @@ MediaDecoderStateMachine::InitiateSeek(S
 
   mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
 
   // Reset our state machine and decoding pipeline before seeking.
   Reset();
 
   // Do the seek.
   RefPtr<MediaDecoderStateMachine> self = this;
-  SeekTarget seekTarget = mCurrentSeek.mTarget;
-  seekTarget.SetTime(seekTarget.GetTime() + media::TimeUnit::FromMicroseconds(StartTime()));
-  mSeekRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
-                                 &MediaDecoderReader::Seek, seekTarget,
-                                 Duration().ToMicroseconds())
+  mSeekRequest.Begin(
+    mReaderWrapper->Seek(mCurrentSeek.mTarget, Duration())
     ->Then(OwnerThread(), __func__,
            [self] (media::TimeUnit) -> void {
              self->mSeekRequest.Complete();
              // We must decode the first samples of active streams, so we can determine
              // the new stream time. So dispatch tasks to do that.
              self->EnsureAudioDecodeTaskQueued();
              self->EnsureVideoDecodeTaskQueued();
            }, [self] (nsresult aResult) -> void {
@@ -1698,35 +1690,21 @@ MediaDecoderStateMachine::EnsureAudioDec
 void
 MediaDecoderStateMachine::RequestAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
-  if (mSentFirstFrameLoadedEvent) {
-    mAudioDataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(),
-                                        __func__, &MediaDecoderReader::RequestAudioData)
-      ->Then(OwnerThread(), __func__, this,
-             &MediaDecoderStateMachine::OnAudioDecoded,
-             &MediaDecoderStateMachine::OnAudioNotDecoded));
-  } else {
-    mAudioDataRequest.Begin(
-      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
-                  &MediaDecoderReader::RequestAudioData)
-      ->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
-             &StartTimeRendezvous::ProcessFirstSample<AudioDataPromise, MediaData::AUDIO_DATA>,
-             &StartTimeRendezvous::FirstSampleRejected<MediaData::AUDIO_DATA>)
-      ->CompletionPromise()
-      ->Then(OwnerThread(), __func__, this,
-             &MediaDecoderStateMachine::OnAudioDecoded,
-             &MediaDecoderStateMachine::OnAudioNotDecoded)
-    );
-  }
+  mAudioDataRequest.Begin(
+    mReaderWrapper->RequestAudioData()
+    ->Then(OwnerThread(), __func__, this,
+           &MediaDecoderStateMachine::OnAudioDecoded,
+           &MediaDecoderStateMachine::OnAudioNotDecoded));
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (IsShutdown()) {
@@ -1771,54 +1749,33 @@ MediaDecoderStateMachine::RequestVideoDa
   // Time the video decode, so that if it's slow, we can increase our low
   // audio threshold to reduce the chance of an audio underrun while we're
   // waiting for a video decode to complete.
   TimeStamp videoDecodeStartTime = TimeStamp::Now();
 
   bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent &&
     NeedToSkipToNextKeyframe();
 
-  int64_t currentTime =
-    mState == DECODER_STATE_SEEKING || !mSentFirstFrameLoadedEvent
-      ? 0 : GetMediaTime() + StartTime();
+  media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(
+    mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime());
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
-             currentTime);
+             currentTime.ToMicroseconds());
 
   RefPtr<MediaDecoderStateMachine> self = this;
-  if (mSentFirstFrameLoadedEvent) {
-    mVideoDataRequest.Begin(
-      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
-                  &MediaDecoderReader::RequestVideoData,
-                  skipToNextKeyFrame, currentTime)
-      ->Then(OwnerThread(), __func__,
-             [self, videoDecodeStartTime] (MediaData* aVideoSample) {
-               self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
-             },
-             [self] (MediaDecoderReader::NotDecodedReason aReason) {
-               self->OnVideoNotDecoded(aReason);
-             }));
-  } else {
-    mVideoDataRequest.Begin(
-      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
-                  &MediaDecoderReader::RequestVideoData,
-                  skipToNextKeyFrame, currentTime)
-      ->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
-             &StartTimeRendezvous::ProcessFirstSample<VideoDataPromise, MediaData::VIDEO_DATA>,
-             &StartTimeRendezvous::FirstSampleRejected<MediaData::VIDEO_DATA>)
-      ->CompletionPromise()
-      ->Then(OwnerThread(), __func__,
-             [self, videoDecodeStartTime] (MediaData* aVideoSample) {
-               self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
-             },
-             [self] (MediaDecoderReader::NotDecodedReason aReason) {
-               self->OnVideoNotDecoded(aReason);
-             }));
-  }
+  mVideoDataRequest.Begin(
+    mReaderWrapper->RequestVideoData(skipToNextKeyFrame, currentTime)
+    ->Then(OwnerThread(), __func__,
+           [self, videoDecodeStartTime] (MediaData* aVideoSample) {
+             self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
+           },
+           [self] (MediaDecoderReader::NotDecodedReason aReason) {
+             self->OnVideoNotDecoded(aReason);
+           }));
 }
 
 void
 MediaDecoderStateMachine::StartMediaSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMediaSink->IsStarted()) {
     mAudioCompleted = false;
@@ -1941,39 +1898,24 @@ MediaDecoderStateMachine::OnMetadataRead
   }
 
   // Set mode to PLAYBACK after reading metadata.
   mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
   mInfo = aMetadata->mInfo;
   mMetadataTags = aMetadata->mTags.forget();
   RefPtr<MediaDecoderStateMachine> self = this;
 
-  // Set up the start time rendezvous if it doesn't already exist (which is
-  // generally the case, unless we're coming out of dormant mode).
-  if (!mStartTimeRendezvous) {
-    mStartTimeRendezvous = new StartTimeRendezvous(OwnerThread(), HasAudio(), HasVideo(),
-                                                   mReader->ForceZeroStartTime() || IsRealTime());
-
-    mStartTimeRendezvous->AwaitStartTime()->Then(OwnerThread(), __func__,
-      [self] () -> void {
-        NS_ENSURE_TRUE_VOID(!self->IsShutdown());
-        self->mReader->DispatchSetStartTime(self->StartTime());
-      },
-      [] () -> void { NS_WARNING("Setting start time on reader failed"); }
-    );
-  }
-
   if (mInfo.mMetadataDuration.isSome()) {
     RecomputeDuration();
   } else if (mInfo.mUnadjustedMetadataEndTime.isSome()) {
-    mStartTimeRendezvous->AwaitStartTime()->Then(OwnerThread(), __func__,
+    mReaderWrapper->AwaitStartTime()->Then(OwnerThread(), __func__,
       [self] () -> void {
         NS_ENSURE_TRUE_VOID(!self->IsShutdown());
         TimeUnit unadjusted = self->mInfo.mUnadjustedMetadataEndTime.ref();
-        TimeUnit adjustment = TimeUnit::FromMicroseconds(self->StartTime());
+        TimeUnit adjustment = self->mReaderWrapper->StartTime();
         self->mInfo.mMetadataDuration.emplace(unadjusted - adjustment);
         self->RecomputeDuration();
       }, [] () -> void { NS_WARNING("Adjusting metadata end time failed"); }
     );
   }
 
   if (HasVideo()) {
     DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d",
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -99,16 +99,17 @@ hardware (via AudioStream).
 namespace mozilla {
 
 namespace media {
 class MediaSink;
 }
 
 class AudioSegment;
 class DecodedStream;
+class MediaDecoderReaderWrapper;
 class OutputStreamManager;
 class TaskQueue;
 
 extern LazyLogModule gMediaDecoderLog;
 extern LazyLogModule gMediaSampleLog;
 
 enum class MediaEventType : int8_t {
   PlaybackStarted,
@@ -689,143 +690,16 @@ private:
 
   // True if we've dispatched a task to run the state machine but the task has
   // yet to run.
   bool mDispatchedStateMachine;
 
   // Used to dispatch another round schedule with specific target time.
   DelayedScheduler mDelayedScheduler;
 
-  // StartTimeRendezvous is a helper class that quarantines the first sample
-  // until it gets a sample from both channels, such that we can be guaranteed
-  // to know the start time by the time On{Audio,Video}Decoded is called.
-  class StartTimeRendezvous {
-  public:
-    typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
-    typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
-    typedef MozPromise<bool, bool, /* isExclusive = */ false> HaveStartTimePromise;
-
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous);
-    StartTimeRendezvous(AbstractThread* aOwnerThread, bool aHasAudio, bool aHasVideo,
-                        bool aForceZeroStartTime)
-      : mOwnerThread(aOwnerThread)
-    {
-      if (aForceZeroStartTime) {
-        mAudioStartTime.emplace(0);
-        mVideoStartTime.emplace(0);
-        return;
-      }
-
-      if (!aHasAudio) {
-        mAudioStartTime.emplace(INT64_MAX);
-      }
-
-      if (!aHasVideo) {
-        mVideoStartTime.emplace(INT64_MAX);
-      }
-    }
-
-    void Destroy()
-    {
-      mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX));
-      mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX));
-      mHaveStartTimePromise.RejectIfExists(false, __func__);
-    }
-
-    RefPtr<HaveStartTimePromise> AwaitStartTime()
-    {
-      if (HaveStartTime()) {
-        return HaveStartTimePromise::CreateAndResolve(true, __func__);
-      }
-      return mHaveStartTimePromise.Ensure(__func__);
-    }
-
-    template<typename PromiseType>
-    struct PromiseSampleType {
-      typedef typename PromiseType::ResolveValueType::element_type Type;
-    };
-
-    template<typename PromiseType, MediaData::Type SampleType>
-    RefPtr<PromiseType> ProcessFirstSample(typename PromiseSampleType<PromiseType>::Type* aData)
-    {
-      typedef typename PromiseSampleType<PromiseType>::Type DataType;
-      typedef typename PromiseType::Private PromisePrivate;
-      MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-
-      MaybeSetChannelStartTime<SampleType>(aData->mTime);
-
-      RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
-      RefPtr<DataType> data = aData;
-      RefPtr<StartTimeRendezvous> self = this;
-      AwaitStartTime()->Then(mOwnerThread, __func__,
-                             [p, data, self] () -> void {
-                               MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn());
-                               p->Resolve(data, __func__);
-                             },
-                             [p] () -> void { p->Reject(MediaDecoderReader::CANCELED, __func__); });
-
-      return p.forget();
-    }
-
-    template<MediaData::Type SampleType>
-    void FirstSampleRejected(MediaDecoderReader::NotDecodedReason aReason)
-    {
-      MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-      if (aReason == MediaDecoderReader::DECODE_ERROR) {
-        mHaveStartTimePromise.RejectIfExists(false, __func__);
-      } else if (aReason == MediaDecoderReader::END_OF_STREAM) {
-        MOZ_LOG(gMediaDecoderLog, LogLevel::Debug,
-                ("StartTimeRendezvous=%p SampleType(%d) Has no samples.", this, SampleType));
-        MaybeSetChannelStartTime<SampleType>(INT64_MAX);
-      }
-    }
-
-    bool HaveStartTime() { return mAudioStartTime.isSome() && mVideoStartTime.isSome(); }
-    int64_t StartTime()
-    {
-      int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref());
-      return time == INT64_MAX ? 0 : time;
-    }
-  private:
-    virtual ~StartTimeRendezvous() {}
-
-    template<MediaData::Type SampleType>
-    void MaybeSetChannelStartTime(int64_t aStartTime)
-    {
-      if (ChannelStartTime(SampleType).isSome()) {
-        // If we're initialized with aForceZeroStartTime=true, the channel start
-        // times are already set.
-        return;
-      }
-
-      MOZ_LOG(gMediaDecoderLog, LogLevel::Debug,
-              ("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld",
-               this, SampleType, aStartTime));
-
-      ChannelStartTime(SampleType).emplace(aStartTime);
-      if (HaveStartTime()) {
-        mHaveStartTimePromise.ResolveIfExists(true, __func__);
-      }
-    }
-
-    Maybe<int64_t>& ChannelStartTime(MediaData::Type aType)
-    {
-      return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime;
-    }
-
-    MozPromiseHolder<HaveStartTimePromise> mHaveStartTimePromise;
-    RefPtr<AbstractThread> mOwnerThread;
-    Maybe<int64_t> mAudioStartTime;
-    Maybe<int64_t> mVideoStartTime;
-  };
-  RefPtr<StartTimeRendezvous> mStartTimeRendezvous;
-
-  bool HaveStartTime() { return mStartTimeRendezvous && mStartTimeRendezvous->HaveStartTime(); }
-  int64_t StartTime() { return mStartTimeRendezvous->StartTime(); }
-
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<MediaData> mAudioQueue;
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
   MediaQueue<MediaData> mVideoQueue;
 
   // The decoder monitor must be obtained before modifying this state.
@@ -878,16 +752,18 @@ private:
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the state machine's constructor.
   const RefPtr<MediaDecoderReader> mReader;
 
+  const RefPtr<MediaDecoderReaderWrapper> mReaderWrapper;
+
   // The end time of the last audio frame that's been pushed onto the media sink
   // in microseconds. This will approximately be the end time
   // of the audio stream, unless another frame is pushed to the hardware.
   int64_t AudioEndTime() const;
 
   // The end time of the last rendered video frame that's been sent to
   // compositor.
   int64_t VideoEndTime() const;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -209,16 +209,17 @@ UNIFIED_SOURCES += [
     'FlushableTaskQueue.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
+    'MediaDecoderReaderWrapper.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaDeviceInfo.cpp',
     'MediaDevices.cpp',
     'MediaFormatReader.cpp',
     'MediaManager.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -86,16 +86,23 @@ public:
       // if the key becomes usable again.
       Input(aDecrypted.mSample);
     } else if (GMP_FAILED(aDecrypted.mStatus)) {
       if (mCallback) {
         mCallback->Error();
       }
     } else {
       MOZ_ASSERT(!mIsShutdown);
+      // The Adobe GMP AAC decoder gets confused if we pass it non-encrypted
+      // samples with valid crypto data. So clear the crypto data, since the
+      // sample should be decrypted now anyway. If we don't do this and we're
+      // using the Adobe GMP for unencrypted decoding of data that is decrypted
+      // by gmp-clearkey, decoding will fail.
+      UniquePtr<MediaRawDataWriter> writer(aDecrypted.mSample->CreateWriter());
+      writer->mCrypto = CryptoSample();
       nsresult rv = mDecoder->Input(aDecrypted.mSample);
       Unused << NS_WARN_IF(NS_FAILED(rv));
     }
   }
 
   nsresult Flush() override {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
--- a/dom/media/test/external/external_media_harness/testcase.py
+++ b/dom/media/test/external/external_media_harness/testcase.py
@@ -117,26 +117,25 @@ class NetworkBandwidthTestCase(MediaTest
         BrowserMobProxyTestCaseMixin.setUp(self)
         self.proxy = self.create_browsermob_proxy()
 
     def tearDown(self):
         super(NetworkBandwidthTestCase, self).tearDown()
         BrowserMobProxyTestCaseMixin.tearDown(self)
         self.proxy = None
 
-    def run_videos(self):
+    def run_videos(self, timeout=60):
         """
         Run each of the videos in the video list. Raises if something goes
         wrong in playback.
         """
         with self.marionette.using_context('content'):
             for url in self.video_urls:
-                video = VP(self.marionette, url,
-                                       stall_wait_time=60,
-                                       set_duration=60)
+                video = VP(self.marionette, url, stall_wait_time=60,
+                           set_duration=60, timeout=timeout)
                 self.run_playback(video)
 
 
 class VideoPlaybackTestsMixin(object):
 
     """
     Test MSE playback in HTML5 video element.
 
@@ -183,25 +182,25 @@ class VideoPlaybackTestsMixin(object):
 class NetworkBandwidthTestsMixin(object):
 
     """
         Test video urls with various bandwidth settings.
     """
 
     def test_playback_limiting_bandwidth_250(self):
         self.proxy.limits({'downstream_kbps': 250})
-        self.run_videos()
+        self.run_videos(timeout=120)
 
     def test_playback_limiting_bandwidth_500(self):
         self.proxy.limits({'downstream_kbps': 500})
-        self.run_videos()
+        self.run_videos(timeout=120)
 
     def test_playback_limiting_bandwidth_1000(self):
         self.proxy.limits({'downstream_kbps': 1000})
-        self.run_videos()
+        self.run_videos(timeout=120)
 
 
 reset_adobe_gmp_script = """
 navigator.requestMediaKeySystemAccess('com.adobe.primetime',
 [{initDataType: 'cenc'}]).then(
     function(access) {
         marionetteScriptFinished('success');
     },
--- a/dom/media/test/external/external_media_tests/playback/test_ultra_low_bandwidth.py
+++ b/dom/media/test/external/external_media_tests/playback/test_ultra_low_bandwidth.py
@@ -7,9 +7,9 @@ from marionette import BrowserMobProxyTe
 from external_media_harness.testcase import NetworkBandwidthTestCase
 
 
 class TestUltraLowBandwidth(NetworkBandwidthTestCase,
                                     BrowserMobProxyTestCaseMixin):
 
     def test_playback_limiting_bandwidth_160(self):
         self.proxy.limits({'downstream_kbps': 160})
-        self.run_videos()
+        self.run_videos(timeout=120)
new file mode 100644
--- /dev/null
+++ b/dom/media/test/external/requirements-docs.txt
@@ -0,0 +1,5 @@
+sphinx
+sphinx-rtd-theme
+
+-e dom/media/test/external
+
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -44,16 +44,17 @@ GetUserMediaLog()
 
 namespace mozilla {
 
 // statics from AudioInputCubeb
 nsTArray<int>* AudioInputCubeb::mDeviceIndexes;
 nsTArray<nsCString>* AudioInputCubeb::mDeviceNames;
 cubeb_device_collection* AudioInputCubeb::mDevices = nullptr;
 bool AudioInputCubeb::mAnyInUse = false;
+StaticMutex AudioInputCubeb::sMutex;
 
 // AudioDeviceID is an annoying opaque value that's really a string
 // pointer, and is freed when the cubeb_device_collection is destroyed
 
 void AudioInputCubeb::UpdateDeviceList()
 {
   cubeb_device_collection *devices = nullptr;
 
@@ -86,16 +87,17 @@ void AudioInputCubeb::UpdateDeviceList()
         (*mDeviceIndexes)[j] = i;
       } else {
         // new device, add to the array
         mDeviceIndexes->AppendElement(i);
         mDeviceNames->AppendElement(devices->device[i]->device_id);
       }
     }
   }
+  StaticMutexAutoLock lock(sMutex);
   // swap state
   if (mDevices) {
     cubeb_device_collection_destroy(mDevices);
   }
   mDevices = devices;
 }
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -8,16 +8,17 @@
 #include "prcvar.h"
 #include "prthread.h"
 #include "prprf.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "DOMMediaStream.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsRefPtrHashtable.h"
@@ -190,18 +191,25 @@ public:
     }
     if (aIndex >= (int) mDeviceIndexes->Length()) {
       return -1;
     }
     // Note: if the device is gone, this will be -1
     return (*mDeviceIndexes)[aIndex]; // translate to mDevices index
   }
 
+  static StaticMutex& Mutex()
+  {
+    return sMutex;
+  }
+
   static bool GetDeviceID(int aDeviceIndex, CubebUtils::AudioDeviceID &aID)
   {
+    // Assert sMutex is held
+    sMutex.AssertCurrentThreadOwns();
     int dev_index = DeviceIndex(aDeviceIndex);
     if (dev_index != -1) {
       aID = mDevices->device[dev_index]->devid;
       return true;
     }
     return false;
   }
 
@@ -275,16 +283,17 @@ private:
   int mSelectedDevice;
   uint32_t mInUseCount;
 
   // pointers to avoid static constructors
   static nsTArray<int>* mDeviceIndexes;
   static nsTArray<nsCString>* mDeviceNames;
   static cubeb_device_collection *mDevices;
   static bool mAnyInUse;
+  static StaticMutex sMutex;
 };
 
 class AudioInputWebRTC final : public AudioInput
 {
 public:
   explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {}
 
   int GetNumOfRecordingDevices(int& aDevices)
--- a/dom/media/webspeech/synth/SpeechSynthesis.cpp
+++ b/dom/media/webspeech/synth/SpeechSynthesis.cpp
@@ -26,80 +26,65 @@ GetSpeechSynthLog()
 }
 #define LOG(type, msg) MOZ_LOG(GetSpeechSynthLog(), type, msg)
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(SpeechSynthesis)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SpeechSynthesis)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SpeechSynthesis, DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentTask)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechQueue)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->mVoiceCache.Clear();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SpeechSynthesis)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SpeechSynthesis, DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentTask)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechQueue)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   for (auto iter = tmp->mVoiceCache.Iter(); !iter.Done(); iter.Next()) {
     SpeechSynthesisVoice* voice = iter.UserData();
     cb.NoteXPCOMChild(voice);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SpeechSynthesis)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechSynthesis)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SpeechSynthesis)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-NS_INTERFACE_MAP_END
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechSynthesis)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechSynthesis)
+NS_IMPL_ADDREF_INHERITED(SpeechSynthesis, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(SpeechSynthesis, DOMEventTargetHelper)
 
 SpeechSynthesis::SpeechSynthesis(nsPIDOMWindowInner* aParent)
-  : mParent(aParent)
+  : DOMEventTargetHelper(aParent)
   , mHoldQueue(false)
   , mInnerID(aParent->WindowID())
 {
   MOZ_ASSERT(aParent->IsInnerWindow());
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(this, "inner-window-destroyed", true);
+    obs->AddObserver(this, "synth-voices-changed", true);
   }
 
 }
 
 SpeechSynthesis::~SpeechSynthesis()
 {
 }
 
 JSObject*
 SpeechSynthesis::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SpeechSynthesisBinding::Wrap(aCx, this, aGivenProto);
 }
 
-nsPIDOMWindowInner*
-SpeechSynthesis::GetParentObject() const
-{
-  return mParent;
-}
-
 bool
 SpeechSynthesis::Pending() const
 {
   switch (mSpeechQueue.Length()) {
   case 0:
     return false;
 
   case 1:
@@ -130,28 +115,43 @@ SpeechSynthesis::Paused() const
 }
 
 bool
 SpeechSynthesis::HasEmptyQueue() const
 {
   return mSpeechQueue.Length() == 0;
 }
 
+bool SpeechSynthesis::HasVoices() const
+{
+  uint32_t voiceCount = mVoiceCache.Count();
+  if (voiceCount == 0) {
+    nsresult rv = nsSynthVoiceRegistry::GetInstance()->GetVoiceCount(&voiceCount);
+    if(NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+  }
+
+  return voiceCount != 0;
+}
+
 void
 SpeechSynthesis::Speak(SpeechSynthesisUtterance& aUtterance)
 {
   if (aUtterance.mState != SpeechSynthesisUtterance::STATE_NONE) {
     // XXX: Should probably raise an error
     return;
   }
 
   mSpeechQueue.AppendElement(&aUtterance);
   aUtterance.mState = SpeechSynthesisUtterance::STATE_PENDING;
 
-  if (mSpeechQueue.Length() == 1 && !mCurrentTask && !mHoldQueue) {
+  // If we only have one item in the queue, we aren't pre-paused, and
+  // we have voices available, speak it.
+  if (mSpeechQueue.Length() == 1 && !mCurrentTask && !mHoldQueue && HasVoices()) {
     AdvanceQueue();
   }
 }
 
 void
 SpeechSynthesis::AdvanceQueue()
 {
   LOG(LogLevel::Debug,
@@ -159,17 +159,18 @@ SpeechSynthesis::AdvanceQueue()
 
   if (mSpeechQueue.IsEmpty()) {
     return;
   }
 
   RefPtr<SpeechSynthesisUtterance> utterance = mSpeechQueue.ElementAt(0);
 
   nsAutoString docLang;
-  nsIDocument* doc = mParent->GetExtantDoc();
+  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
+  nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
 
   if (doc) {
     Element* elm = doc->GetHtmlElement();
 
     if (elm) {
       elm->GetLang(docLang);
     }
   }
@@ -294,35 +295,39 @@ SpeechSynthesis::ForceEnd()
 }
 
 NS_IMETHODIMP
 SpeechSynthesis::Observe(nsISupports* aSubject, const char* aTopic,
                          const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (strcmp(aTopic, "inner-window-destroyed")) {
-    return NS_OK;
-  }
+
+  if (strcmp(aTopic, "inner-window-destroyed") == 0) {
+    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
-  nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
-  NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+    uint64_t innerID;
+    nsresult rv = wrapper->GetData(&innerID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (innerID == mInnerID) {
+      Cancel();
 
-  uint64_t innerID;
-  nsresult rv = wrapper->GetData(&innerID);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (innerID == mInnerID) {
-    if (mCurrentTask) {
-      mCurrentTask->Cancel();
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      if (obs) {
+        obs->RemoveObserver(this, "inner-window-destroyed");
+      }
     }
-
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    if (obs) {
-      obs->RemoveObserver(this, "inner-window-destroyed");
+  } else if (strcmp(aTopic, "synth-voices-changed") == 0) {
+    LOG(LogLevel::Debug, ("SpeechSynthesis::onvoiceschanged"));
+    DispatchTrustedEvent(NS_LITERAL_STRING("voiceschanged"));
+    // If we have a pending item, and voices become available, speak it.
+    if (!mCurrentTask && !mHoldQueue && HasVoices()) {
+      AdvanceQueue();
     }
   }
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/SpeechSynthesis.h
+++ b/dom/media/webspeech/synth/SpeechSynthesis.h
@@ -20,30 +20,27 @@
 
 class nsIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class nsSpeechTask;
 
-class SpeechSynthesis final : public nsIObserver
-                            , public nsWrapperCache
+class SpeechSynthesis final : public DOMEventTargetHelper
+                            , public nsIObserver
                             , public nsSupportsWeakReference
 {
 public:
   explicit SpeechSynthesis(nsPIDOMWindowInner* aParent);
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SpeechSynthesis,
-                                                         nsIObserver)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SpeechSynthesis, DOMEventTargetHelper)
   NS_DECL_NSIOBSERVER
 
-  nsPIDOMWindowInner* GetParentObject() const;
-
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   bool Pending() const;
 
   bool Speaking() const;
 
   bool Paused() const;
 
@@ -58,22 +55,24 @@ public:
   void Resume();
 
   void OnEnd(const nsSpeechTask* aTask);
 
   void GetVoices(nsTArray< RefPtr<SpeechSynthesisVoice> >& aResult);
 
   void ForceEnd();
 
+  IMPL_EVENT_HANDLER(voiceschanged)
+
 private:
   virtual ~SpeechSynthesis();
 
   void AdvanceQueue();
 
-  nsCOMPtr<nsPIDOMWindowInner> mParent;
+  bool HasVoices() const;
 
   nsTArray<RefPtr<SpeechSynthesisUtterance> > mSpeechQueue;
 
   RefPtr<nsSpeechTask> mCurrentTask;
 
   nsRefPtrHashtable<nsStringHashKey, SpeechSynthesisVoice> mVoiceCache;
 
   bool mHoldQueue;
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp
@@ -15,31 +15,31 @@ using namespace mozilla::dom;
   {0x914e73b4, 0x6337, 0x4bef, {0x97, 0xf3, 0x4d, 0x06, 0x9e, 0x05, 0x3a, 0x12}}
 
 #define OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID "@mozilla.org/synthsystem;1"
 
 // Defines OSXSpeechSynthesizerServiceConstructor
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(OSXSpeechSynthesizerService,
                                          OSXSpeechSynthesizerService::GetInstanceForService)
 
-// Defines kSAPISERVICE_CID
+// Defines kOSXSERVICE_CID
 NS_DEFINE_NAMED_CID(OSXSPEECHSYNTHESIZERSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kCIDs[] = {
   { &kOSXSPEECHSYNTHESIZERSERVICE_CID, true, nullptr, OSXSpeechSynthesizerServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kContracts[] = {
   { OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID, &kOSXSPEECHSYNTHESIZERSERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kCategories[] = {
-  { "profile-after-change", "Sapi Speech Synth", OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID },
+  { "speech-synth-started", "OSX Speech Synth", OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID },
   { nullptr }
 };
 
 static void
 UnloadOSXSpeechSynthesizerModule()
 {
   OSXSpeechSynthesizerService::Shutdown();
 }
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -245,16 +245,19 @@ RegisterVoicesRunnable::Run()
     if (NS_WARN_IF(NS_FAILED(rv))) {
       continue;
     }
 
     if (voice.mIsDefault) {
       registry->SetDefaultVoice(voice.mUri, true);
     }
   }
+
+  registry->NotifyVoicesChanged();
+
   return NS_OK;
 }
 
 class EnumVoicesRunnable final : public nsRunnable
 {
 public:
   explicit EnumVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
     : mSpeechService(aSpeechService)
--- a/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
+++ b/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
@@ -28,16 +28,18 @@ child:
     async VoiceAdded(RemoteVoice aVoice);
 
     async VoiceRemoved(nsString aUri);
 
     async SetDefaultVoice(nsString aUri, bool aIsDefault);
 
     async IsSpeakingChanged(bool aIsSpeaking);
 
+    async NotifyVoicesChanged();
+
 parent:
     async __delete__();
 
     async PSpeechSynthesisRequest(nsString aText, nsString aUri, nsString aLang,
                                   float aVolume, float aRate, float aPitch);
 
     sync ReadVoicesAndState() returns (RemoteVoice[] aVoices,
                                        nsString[] aDefaults, bool aIsSpeaking);
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
@@ -42,16 +42,23 @@ SpeechSynthesisChild::RecvSetDefaultVoic
 
 bool
 SpeechSynthesisChild::RecvIsSpeakingChanged(const bool& aIsSpeaking)
 {
   nsSynthVoiceRegistry::RecvIsSpeakingChanged(aIsSpeaking);
   return true;
 }
 
+bool
+SpeechSynthesisChild::RecvNotifyVoicesChanged()
+{
+  nsSynthVoiceRegistry::RecvNotifyVoicesChanged();
+  return true;
+}
+
 PSpeechSynthesisRequestChild*
 SpeechSynthesisChild::AllocPSpeechSynthesisRequestChild(const nsString& aText,
                                                         const nsString& aLang,
                                                         const nsString& aUri,
                                                         const float& aVolume,
                                                         const float& aRate,
                                                         const float& aPitch)
 {
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
@@ -25,16 +25,18 @@ public:
   bool RecvVoiceAdded(const RemoteVoice& aVoice) override;
 
   bool RecvVoiceRemoved(const nsString& aUri) override;
 
   bool RecvSetDefaultVoice(const nsString& aUri, const bool& aIsDefault) override;
 
   bool RecvIsSpeakingChanged(const bool& aIsSpeaking) override;
 
+  bool RecvNotifyVoicesChanged() override;
+
 protected:
   SpeechSynthesisChild();
   virtual ~SpeechSynthesisChild();
 
   PSpeechSynthesisRequestChild* AllocPSpeechSynthesisRequestChild(const nsString& aLang,
                                                                   const nsString& aUri,
                                                                   const nsString& aText,
                                                                   const float& aVolume,
--- a/dom/media/webspeech/synth/moz.build
+++ b/dom/media/webspeech/synth/moz.build
@@ -1,16 +1,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_WEBSPEECH']:
     MOCHITEST_MANIFESTS += [
         'test/mochitest.ini',
+        'test/startup/mochitest.ini',
     ]
 
     XPIDL_MODULE = 'dom_webspeechsynth'
 
     XPIDL_SOURCES += [
         'nsISpeechService.idl',
         'nsISynthVoiceRegistry.idl'
     ]
--- a/dom/media/webspeech/synth/nsISpeechService.idl
+++ b/dom/media/webspeech/synth/nsISpeechService.idl
@@ -160,8 +160,14 @@ interface nsISpeechService : nsISupports
              in float aVolume, in float aRate, in float aPitch,
              in nsISpeechTask aTask);
 
   const SpeechServiceType SERVICETYPE_DIRECT_AUDIO = 1;
   const SpeechServiceType SERVICETYPE_INDIRECT_AUDIO = 2;
 
   readonly attribute SpeechServiceType serviceType;
 };
+
+%{C++
+// This is the service category speech services could use to start up as
+// a component.
+#define NS_SPEECH_SYNTH_STARTED "speech-synth-started"
+%}
--- a/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
+++ b/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsISpeechService;
 
-[scriptable, builtinclass, uuid(dac09c3a-156e-4025-a4ab-bc88b0ea92e7)]
+[scriptable, builtinclass, uuid(5d7a0b38-77e5-4ee5-897c-ce5db9b85d44)]
 interface nsISynthVoiceRegistry : nsISupports
 {
   /**
    * Register a speech synthesis voice.
    *
    * @param aService          the service that provides this voice.
    * @param aUri              a unique identifier for this voice.
    * @param aName             human-readable name for this voice.
@@ -28,16 +28,22 @@ interface nsISynthVoiceRegistry : nsISup
    * Remove a speech synthesis voice.
    *
    * @param aService the service that was used to add the voice.
    * @param aUri     a unique identifier of an existing voice.
    */
   void removeVoice(in nsISpeechService aService, in DOMString aUri);
 
   /**
+   * Notify content of voice availability changes. This allows content
+   * to be notified of voice catalog changes in real time.
+   */
+  void notifyVoicesChanged();
+
+  /**
    * Set a voice as default.
    *
    * @param aUri       a unique identifier of an existing voice.
    * @param aIsDefault true if this voice should be toggled as default.
    */
   void setDefaultVoice(in DOMString aUri, in boolean aIsDefault);
 
   readonly attribute uint32_t voiceCount;
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsILocaleService.h"
 #include "nsISpeechService.h"
 #include "nsServiceManagerUtils.h"
+#include "nsCategoryManagerUtils.h"
 
 #include "SpeechSynthesisUtterance.h"
 #include "SpeechSynthesisVoice.h"
 #include "nsSynthVoiceRegistry.h"
 #include "nsSpeechTask.h"
 #include "AudioChannelService.h"
 
 #include "nsString.h"
@@ -185,16 +186,21 @@ nsSynthVoiceRegistry*
 nsSynthVoiceRegistry::GetInstance()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gSynthVoiceRegistry) {
     gSynthVoiceRegistry = new nsSynthVoiceRegistry();
     Preferences::AddBoolVarCache(&sForceGlobalQueue,
                                  "media.webspeech.synth.force_global_queue");
+    if (XRE_IsParentProcess()) {
+      // Start up all speech synth services.
+      NS_CreateServicesFromCategory(NS_SPEECH_SYNTH_STARTED, nullptr,
+        NS_SPEECH_SYNTH_STARTED);
+    }
   }
 
   return gSynthVoiceRegistry;
 }
 
 already_AddRefed<nsSynthVoiceRegistry>
 nsSynthVoiceRegistry::GetInstanceForService()
 {
@@ -275,16 +281,27 @@ nsSynthVoiceRegistry::RecvIsSpeakingChan
   // speaking state on construction.
   if(!gSynthVoiceRegistry) {
     return;
   }
 
   gSynthVoiceRegistry->mIsSpeaking = aIsSpeaking;
 }
 
+void
+nsSynthVoiceRegistry::RecvNotifyVoicesChanged()
+{
+  // If we dont have a local instance of the registry yet, we don't care.
+  if(!gSynthVoiceRegistry) {
+    return;
+  }
+
+  gSynthVoiceRegistry->NotifyVoicesChanged();
+}
+
 NS_IMETHODIMP
 nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
                                const nsAString& aUri,
                                const nsAString& aName,
                                const nsAString& aLang,
                                bool aLocalService,
                                bool aQueuesUtterances)
 {
@@ -346,16 +363,37 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpe
 
   for (uint32_t i = 0; i < ssplist.Length(); ++i)
     Unused << ssplist[i]->SendVoiceRemoved(nsString(aUri));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSynthVoiceRegistry::NotifyVoicesChanged()
+{
+  if (XRE_IsParentProcess()) {
+    nsTArray<SpeechSynthesisParent*> ssplist;
+    GetAllSpeechSynthActors(ssplist);
+
+    for (uint32_t i = 0; i < ssplist.Length(); ++i)
+      Unused << ssplist[i]->SendNotifyVoicesChanged();
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if(NS_WARN_IF(!(obs))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  obs->NotifyObservers(nullptr, "synth-voices-changed", nullptr);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri,
                                       bool aIsDefault)
 {
   bool found = false;
   VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
   if(NS_WARN_IF(!(found))) {
     return NS_ERROR_NOT_AVAILABLE;
   }
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
@@ -59,16 +59,18 @@ public:
   static void RecvRemoveVoice(const nsAString& aUri);
 
   static void RecvAddVoice(const RemoteVoice& aVoice);
 
   static void RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault);
 
   static void RecvIsSpeakingChanged(bool aIsSpeaking);
 
+  static void RecvNotifyVoicesChanged();
+
   static void Shutdown();
 
 private:
   virtual ~nsSynthVoiceRegistry();
 
   VoiceData* FindBestMatch(const nsAString& aUri, const nsAString& lang);
 
   bool FindVoiceByLang(const nsAString& aLang, VoiceData** aRetval);
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
@@ -28,17 +28,17 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kContracts[] = {
   { SPEECHDISPATCHERSERVICE_CONTRACTID, &kSPEECHDISPATCHERSERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kCategories[] = {
-  { "profile-after-change", "SpeechDispatcher Speech Synth", SPEECHDISPATCHERSERVICE_CONTRACTID },
+  { "speech-synth-started", "SpeechDispatcher Speech Synth", SPEECHDISPATCHERSERVICE_CONTRACTID },
   { nullptr }
 };
 
 static void
 UnloadSpeechDispatcherModule()
 {
   SpeechDispatcherService::Shutdown();
 }
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
@@ -417,17 +417,17 @@ SpeechDispatcherService::Setup()
   //mInitialized = true;
 }
 
 // private methods
 
 void
 SpeechDispatcherService::RegisterVoices()
 {
-  nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
+  RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
   for (auto iter = mVoices.Iter(); !iter.Done(); iter.Next()) {
     RefPtr<SpeechDispatcherVoice>& voice = iter.Data();
 
     // This service can only speak one utterance at a time, so we set
     // aQueuesUtterances to true in order to track global state and schedule
     // access to this service.
     DebugOnly<nsresult> rv =
       registry->AddVoice(this, iter.Key(), voice->mName, voice->mLanguage,
@@ -435,16 +435,18 @@ SpeechDispatcherService::RegisterVoices(
 
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice");
   }
 
   mInitThread->Shutdown();
   mInitThread = nullptr;
 
   mInitialized = true;
+
+  registry->NotifyVoicesChanged();
 }
 
 // nsIObserver
 
 NS_IMETHODIMP
 SpeechDispatcherService::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData)
 {
--- a/dom/media/webspeech/synth/test/FakeSynthModule.cpp
+++ b/dom/media/webspeech/synth/test/FakeSynthModule.cpp
@@ -27,17 +27,17 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kContracts[] = {
   { FAKESYNTHSERVICE_CONTRACTID, &kFAKESYNTHSERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kCategories[] = {
-  { "profile-after-change", "Fake Speech Synth", FAKESYNTHSERVICE_CONTRACTID },
+  { "speech-synth-started", "Fake Speech Synth", FAKESYNTHSERVICE_CONTRACTID },
   { nullptr }
 };
 
 static void
 UnloadFakeSynthmodule()
 {
   nsFakeSynthServices::Shutdown();
 }
--- a/dom/media/webspeech/synth/test/common.js
+++ b/dom/media/webspeech/synth/test/common.js
@@ -53,17 +53,38 @@ function synthTestQueue(aTestArgs, aEndF
 
 function loadFrame(frameId) {
   return new Promise(function(resolve, reject) {
     var frame = document.getElementById(frameId);
     frame.addEventListener('load', function (e) {
       frame.contentWindow.document.title = frameId;
       resolve(frame);
     });
-    frame1.src = 'data:text/html,' + encodeURI('<html><head></head><body></body></html>');
+    frame.src = 'data:text/html,' + encodeURI('<html><head></head><body></body></html>');
+  });
+}
+
+function waitForVoices(win) {
+  return new Promise(resolve => {
+    function resolver() {
+      if (win.speechSynthesis.getVoices().length) {
+        win.speechSynthesis.removeEventListener('voiceschanged', resolver);
+        resolve();
+      }
+    }
+
+    win.speechSynthesis.addEventListener('voiceschanged', resolver);
+    resolver();
+  });
+}
+
+function loadSpeechTest(fileName, prefs, frameId="testFrame") {
+  loadFrame(frameId).then(frame => {
+    waitForVoices(frame.contentWindow).then(
+      () => document.getElementById("testFrame").src = fileName);
   });
 }
 
 function testSynthState(win, expectedState) {
   for (var attr in expectedState) {
     is(win.speechSynthesis[attr], expectedState[attr],
       win.document.title + ": '" + attr + '" does not match');
   }
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -313,28 +313,30 @@ nsFakeSynthServices::nsFakeSynthServices
 
 nsFakeSynthServices::~nsFakeSynthServices()
 {
 }
 
 static void
 AddVoices(nsISpeechService* aService, const VoiceDetails* aVoices, uint32_t aLength)
 {
-  nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
+  RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
   for (uint32_t i = 0; i < aLength; i++) {
     NS_ConvertUTF8toUTF16 name(aVoices[i].name);
     NS_ConvertUTF8toUTF16 uri(aVoices[i].uri);
     NS_ConvertUTF8toUTF16 lang(aVoices[i].lang);
     // These services can handle more than one utterance at a time and have
     // several speaking simultaniously. So, aQueuesUtterances == false
     registry->AddVoice(aService, uri, name, lang, true, false);
     if (aVoices[i].defaultVoice) {
       registry->SetDefaultVoice(uri, true);
     }
   }
+
+  registry->NotifyVoicesChanged();
 }
 
 void
 nsFakeSynthServices::Init()
 {
   mDirectService = new FakeDirectAudioSynth();
   AddVoices(mDirectService, sDirectVoices, ArrayLength(sDirectVoices));
 
@@ -344,22 +346,22 @@ nsFakeSynthServices::Init()
 
 // nsIObserver
 
 NS_IMETHODIMP
 nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
                              const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
+  if(NS_WARN_IF(!(!strcmp(aTopic, "speech-synth-started")))) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (Preferences::GetBool("media.webspeech.synth.test")) {
-    Init();
+    NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsFakeSynthServices::Init));
   }
 
   return NS_OK;
 }
 
 // static methods
 
 nsFakeSynthServices*
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/startup/file_voiceschanged.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1254378
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1254378: Web Speech API check all classes are present</title>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+  </script>
+</head>
+<body>
+<script type="application/javascript">
+
+/** Test for Bug 1254378 **/
+
+function onVoicesChanged() {
+  isnot(speechSynthesis.getVoices().length, 0, "Voices added");
+  speechSynthesis.removeEventListener("voiceschanged", onVoicesChanged);
+  SimpleTest.finish();
+}
+
+speechSynthesis.addEventListener("voiceschanged", onVoicesChanged);
+
+is(speechSynthesis.getVoices().length, 0, "No voices added initially");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/startup/mochitest.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+tags=msg
+subsuite = media
+support-files =
+  file_voiceschanged.html
+
+[test_voiceschanged.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/startup/test_voiceschanged.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1254378
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1254378: Emit onvoiceschanged when voices first added</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1254378">Mozilla Bug 1254378</a>
+<p id="display"></p>
+<iframe id="testFrame"></iframe>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1254378 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
+                          function() { document.getElementById("testFrame").src = "file_voiceschanged.html"; });
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webspeech/synth/test/test_bfcache.html
+++ b/dom/media/webspeech/synth/test/test_bfcache.html
@@ -25,23 +25,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 var iframe;
 
 function onDone() {
   SimpleTest.finish();
 }
 
-function doTest() {
-  iframe = document.getElementById("testFrame");
-  iframe.src = "file_bfcache_frame.html";
-}
-
 SpecialPowers.pushPrefEnv({ set: [
   ['media.webspeech.synth.enabled', true],
   ['media.webspeech.synth.force_global_queue', true],
   ['browser.sessionhistory.cache_subframes', true],
-  ['browser.sessionhistory.max_total_viewers', 10]] }, doTest());
+  ['browser.sessionhistory.max_total_viewers', 10]] },
+  function() {
+    loadSpeechTest("file_bfcache_frame.html");
+  });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_global_queue.html
+++ b/dom/media/webspeech/synth/test/test_global_queue.html
@@ -19,16 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
-                                  ['media.webspeech.synth.force_global_queue', true]] },
-                          function() { document.getElementById("testFrame").src = "file_global_queue.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', true]] },
+  function() { loadSpeechTest("file_global_queue.html"); });
 
 </script>
 </pre>
 </body>
 </html>
\ No newline at end of file
--- a/dom/media/webspeech/synth/test/test_global_queue_cancel.html
+++ b/dom/media/webspeech/synth/test/test_global_queue_cancel.html
@@ -19,16 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
-                                  ['media.webspeech.synth.force_global_queue', true]] },
-                          function() { document.getElementById("testFrame").src = "file_global_queue_cancel.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', true]] },
+  function() { loadSpeechTest("file_global_queue_cancel.html"); });
 
 </script>
 </pre>
 </body>
 </html>
\ No newline at end of file
--- a/dom/media/webspeech/synth/test/test_global_queue_pause.html
+++ b/dom/media/webspeech/synth/test/test_global_queue_pause.html
@@ -19,16 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
-                                  ['media.webspeech.synth.force_global_queue', true]] },
-                          function() { document.getElementById("testFrame").src = "file_global_queue_pause.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', true]] },
+  function() { loadSpeechTest("file_global_queue_pause.html"); });
 
 </script>
 </pre>
 </body>
 </html>
\ No newline at end of file
--- a/dom/media/webspeech/synth/test/test_indirect_service_events.html
+++ b/dom/media/webspeech/synth/test/test_indirect_service_events.html
@@ -22,15 +22,15 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 1155034 **/
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv(
   { set: [['media.webspeech.synth.enabled', true],
           ['media.webspeech.synth.force_global_queue', false]] },
-  function() { document.getElementById("testFrame").src = "file_indirect_service_events.html"; });
+  function() { loadSpeechTest("file_indirect_service_events.html"); });
 
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_cancel.html
+++ b/dom/media/webspeech/synth/test/test_speech_cancel.html
@@ -22,14 +22,14 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 1150315 **/
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv(
   { set: [['media.webspeech.synth.enabled', true],
           ['media.webspeech.synth.force_global_queue', false]] },
-  function() { document.getElementById("testFrame").src = "file_speech_cancel.html"; });
+  function() { loadSpeechTest("file_speech_cancel.html"); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_error.html
+++ b/dom/media/webspeech/synth/test/test_speech_error.html
@@ -22,14 +22,14 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 1226015 **/
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv(
   { set: [['media.webspeech.synth.enabled', true],
           ['media.webspeech.synth.force_global_queue', false]] },
-  function() { document.getElementById("testFrame").src = "file_speech_error.html"; });
+  function() { loadSpeechTest("file_speech_error.html"); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_queue.html
+++ b/dom/media/webspeech/synth/test/test_speech_queue.html
@@ -22,14 +22,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv(
   { set: [['media.webspeech.synth.enabled', true],
           ['media.webspeech.synth.force_global_queue', false]] },
-  function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
+  function() {
+    loadSpeechTest("file_speech_queue.html");
+  });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_simple.html
+++ b/dom/media/webspeech/synth/test/test_speech_simple.html
@@ -19,15 +19,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_simple.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true]] },
+  function() { loadSpeechTest("file_speech_simple.html"); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/windows/SapiModule.cpp
+++ b/dom/media/webspeech/synth/windows/SapiModule.cpp
@@ -29,17 +29,17 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kContracts[] = {
   { SAPISERVICE_CONTRACTID, &kSAPISERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kCategories[] = {
-  { "profile-after-change", "Sapi Speech Synth", SAPISERVICE_CONTRACTID },
+  { "speech-synth-started", "Sapi Speech Synth", SAPISERVICE_CONTRACTID },
   { nullptr }
 };
 
 static void
 UnloadSapiModule()
 {
   SapiService::Shutdown();
 }
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -308,16 +308,18 @@ SapiService::RegisterVoices()
     CoTaskMemFree(description);
     if (NS_FAILED(rv)) {
       continue;
     }
 
     mVoices.Put(uri, voiceToken);
   }
 
+  registry->NotifyVoicesChanged();
+
   return true;
 }
 
 NS_IMETHODIMP
 SapiService::Speak(const nsAString& aText, const nsAString& aUri,
                    float aVolume, float aRate, float aPitch,
                    nsISpeechTask* aTask)
 {
--- a/dom/smil/nsSMILMappedAttribute.cpp
+++ b/dom/smil/nsSMILMappedAttribute.cpp
@@ -141,10 +141,10 @@ nsSMILMappedAttribute::FlushChangesToTar
       shell->RestyleForAnimation(mElement, eRestyle_Self);
     }
   }
 }
 
 already_AddRefed<nsIAtom>
 nsSMILMappedAttribute::GetAttrNameAtom() const
 {
-  return do_GetAtom(nsCSSProps::GetStringValue(mPropID));
+  return NS_Atomize(nsCSSProps::GetStringValue(mPropID));
 }
--- a/dom/smil/nsSMILParserUtils.cpp
+++ b/dom/smil/nsSMILParserUtils.cpp
@@ -346,17 +346,17 @@ MoveToNextToken(RangedPtr<const char16_t
 }
 
 already_AddRefed<nsIAtom>
 ConvertUnescapedTokenToAtom(const nsAString& aToken)
 {
   // Whether the token is an id-ref or event-symbol it should be a valid NCName
   if (aToken.IsEmpty() || NS_FAILED(nsContentUtils::CheckQName(aToken, false)))
     return nullptr;
-  return do_GetAtom(aToken);
+  return NS_Atomize(aToken);
 }
     
 already_AddRefed<nsIAtom>
 ConvertTokenToAtom(const nsAString& aToken,
                    bool aUnescapeToken)
 {
   // Unescaping involves making a copy of the string which we'd like to avoid if possible
   if (!aUnescapeToken) {
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -522,17 +522,17 @@ nsSVGElement::ParseAttribute(int32_t aNa
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGEnum attribute
       EnumAttributesInfo enumInfo = GetEnumInfo();
       for (i = 0; i < enumInfo.mEnumCount; i++) {
         if (aAttribute == *enumInfo.mEnumInfo[i].mName) {
-          nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aValue);
+          nsCOMPtr<nsIAtom> valAtom = NS_Atomize(aValue);
           rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this);
           if (NS_FAILED(rv)) {
             enumInfo.Reset(i);
           } else {
             aResult.SetTo(valAtom);
             didSetResult = true;
           }
           foundMatch = true;
--- a/dom/webidl/SpeechSynthesis.webidl
+++ b/dom/webidl/SpeechSynthesis.webidl
@@ -6,25 +6,27 @@
  * The origin of this IDL file is
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Pref="media.webspeech.synth.enabled"]
-interface SpeechSynthesis {
+interface SpeechSynthesis : EventTarget{
   readonly attribute boolean pending;
   readonly attribute boolean speaking;
   readonly attribute boolean paused;
 
   [UnsafeInPrerendering]
   void speak(SpeechSynthesisUtterance utterance);
   void cancel();
   void pause();
   [UnsafeInPrerendering]
   void resume();
   sequence<SpeechSynthesisVoice> getVoices();
 
+  attribute EventHandler onvoiceschanged;
+
   [ChromeOnly]
   // Force an utterance to end. Circumvents bad speech service implementations.
   void forceEnd();
 };
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -121,15 +121,18 @@ interface XULElement : Element {
 interface MozFrameLoaderOwner {
   [ChromeOnly]
   readonly attribute MozFrameLoader? frameLoader;
 
   [ChromeOnly]
   void setIsPrerendered();
 
   [ChromeOnly, Throws]
-  void swapFrameLoaders(XULElement aOtherOwner);
+  void swapFrameLoaders(XULElement aOtherLoaderOwner);
+
+  [ChromeOnly, Throws]
+  void swapFrameLoaders(HTMLIFrameElement aOtherLoaderOwner);
 };
 
 XULElement implements GlobalEventHandlers;
 XULElement implements TouchEventHandlers;
 XULElement implements MozFrameLoaderOwner;
 XULElement implements OnErrorEventHandlerForNodes;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -550,16 +550,25 @@ LoadJSGCMemoryOptions(const char* aPrefN
     if (memPrefName == matchName ||
         (gRuntimeServiceDuringInit && index == 14)) {
       bool prefValue = GetWorkerPref(matchName, false);
       UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED,
                                  prefValue ? 0 : 1);
       continue;
     }
 
+    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_refresh_frame_slices_enabled");
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 15)) {
+      bool prefValue = GetWorkerPref(matchName, false);
+      UpdateOtherJSGCMemoryOption(rts, JSGC_REFRESH_FRAME_SLICES_ENABLED,
+                                 prefValue ? 0 : 1);
+      continue;
+    }
+
 #ifdef DEBUG
     nsAutoCString message("Workers don't support the 'mem.");
     message.Append(memPrefName);
     message.AppendLiteral("' preference!");
     NS_WARNING(message.get());
 #endif
   }
 }
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -87,16 +87,17 @@ struct JSSettings
     JSSettings_JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN,
     JSSettings_JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX,
     JSSettings_JSGC_HIGH_FREQUENCY_LOW_LIMIT,
     JSSettings_JSGC_HIGH_FREQUENCY_HIGH_LIMIT,
     JSSettings_JSGC_ALLOCATION_THRESHOLD,
     JSSettings_JSGC_SLICE_TIME_BUDGET,
     JSSettings_JSGC_DYNAMIC_HEAP_GROWTH,
     JSSettings_JSGC_DYNAMIC_MARK_SLICE,
+    JSSettings_JSGC_REFRESH_FRAME_SLICES,
     // JSGC_MODE not supported
 
     // This must be last so that we get an accurate count.
     kGCSettingsArraySize
   };
 
   struct JSGCSetting
   {
--- a/dom/xbl/XBLChildrenElement.cpp
+++ b/dom/xbl/XBLChildrenElement.cpp
@@ -45,17 +45,17 @@ XBLChildrenElement::ParseAttribute(int32
                                    nsAttrValue& aResult)
 {
   if (aAttribute == nsGkAtoms::includes &&
       aNamespaceID == kNameSpaceID_None) {
     mIncludes.Clear();
     nsCharSeparatedTokenizer tok(aValue, '|',
                                  nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
     while (tok.hasMoreTokens()) {
-      mIncludes.AppendElement(do_GetAtom(tok.nextToken()));
+      mIncludes.AppendElement(NS_Atomize(tok.nextToken()));
     }
   }
 
   return false;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -847,17 +847,17 @@ nsXBLPrototypeBinding::Read(nsIObjectInp
 
   rv = ReadNamespace(aStream, mBaseNameSpaceID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString baseTag;
   rv = aStream->ReadString(baseTag);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!baseTag.IsEmpty()) {
-    mBaseTag = do_GetAtom(baseTag);
+    mBaseTag = NS_Atomize(baseTag);
   }
 
   mBinding = aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr,
                                    kNameSpaceID_XBL);
 
   nsCOMPtr<nsIContent> child;
   rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -964,18 +964,18 @@ nsXBLPrototypeBinding::Read(nsIObjectInp
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = aStream->ReadString(attrName);
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = aStream->ReadString(attrValue);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsCOMPtr<nsIAtom> atomPrefix = do_GetAtom(attrPrefix);
-      nsCOMPtr<nsIAtom> atomName = do_GetAtom(attrName);
+      nsCOMPtr<nsIAtom> atomPrefix = NS_Atomize(attrPrefix);
+      nsCOMPtr<nsIAtom> atomName = NS_Atomize(attrName);
       mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false);
     }
   }
 
   // Finally, read in the resources.
   while (true) {
     XBLBindingSerializeDetails type;
     rv = aStream->Read8(&type);
@@ -1211,22 +1211,22 @@ nsXBLPrototypeBinding::ReadContentNode(n
 
   // Otherwise, it's an element, so read its tag, attributes and children.
   nsAutoString prefix, tag;
   rv = aStream->ReadString(prefix);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAtom> prefixAtom;
   if (!prefix.IsEmpty())
-    prefixAtom = do_GetAtom(prefix);
+    prefixAtom = NS_Atomize(prefix);
 
   rv = aStream->ReadString(tag);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
+  nsCOMPtr<nsIAtom> tagAtom = NS_Atomize(tag);
   RefPtr<NodeInfo> nodeInfo =
     aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
 
   uint32_t attrCount;
   rv = aStream->Read32(&attrCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create XUL prototype elements, or regular elements for other namespaces.
@@ -1254,24 +1254,24 @@ nsXBLPrototypeBinding::ReadContentNode(n
       nsAutoString prefix, name, val;
       rv = aStream->ReadString(prefix);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = aStream->ReadString(name);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = aStream->ReadString(val);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+      nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(name);
       if (namespaceID == kNameSpaceID_None) {
         attrs[i].mName.SetTo(nameAtom);
       }
       else {
         nsCOMPtr<nsIAtom> prefixAtom;
         if (!prefix.IsEmpty())
-          prefixAtom = do_GetAtom(prefix);
+          prefixAtom = NS_Atomize(prefix);
 
         RefPtr<NodeInfo> ni =
           aNim->GetNodeInfo(nameAtom, prefixAtom,
                             namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
         attrs[i].mName.SetTo(ni);
       }
 
       rv = prototype->SetAttrAt(i, val, documentURI);
@@ -1299,19 +1299,19 @@ nsXBLPrototypeBinding::ReadContentNode(n
       NS_ENSURE_SUCCESS(rv, rv);
       rv = aStream->ReadString(name);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = aStream->ReadString(val);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIAtom> prefixAtom;
       if (!prefix.IsEmpty())
-        prefixAtom = do_GetAtom(prefix);
+        prefixAtom = NS_Atomize(prefix);
 
-      nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+      nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(name);
       content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
     }
 
 #ifdef MOZ_XUL
   }
 #endif
 
   // Now read the attribute forwarding entries (xbl:inherits)
@@ -1324,18 +1324,18 @@ nsXBLPrototypeBinding::ReadContentNode(n
     nsAutoString srcAttribute, destAttribute;
     rv = aStream->ReadString(srcAttribute);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = ReadNamespace(aStream, destNamespaceID);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = aStream->ReadString(destAttribute);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
-    nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
+    nsCOMPtr<nsIAtom> srcAtom = NS_Atomize(srcAttribute);
+    nsCOMPtr<nsIAtom> destAtom = NS_Atomize(destAttribute);
 
     EnsureAttributeTable();
     AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
 
     rv = ReadNamespace(aStream, srcNamespaceID);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -1593,17 +1593,17 @@ nsXBLPrototypeBinding::ResolveBaseBindin
   nsAutoString nameSpace;
 
   if (!prefix.IsEmpty()) {
     mBinding->LookupNamespaceURI(prefix, nameSpace);
     if (!nameSpace.IsEmpty()) {
       int32_t nameSpaceID =
         nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
 
-      nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
+      nsCOMPtr<nsIAtom> tagName = NS_Atomize(display);
       // Check the white list
       if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
         const char16_t* params[] = { display.get() };
         nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                         NS_LITERAL_CSTRING("XBL"), nullptr,
                                         nsContentUtils::eXBL_PROPERTIES,
                                        "InvalidExtendsBinding",
                                         params, ArrayLength(params),
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -222,17 +222,17 @@ nsXBLPrototypeHandler::ExecuteHandler(Ev
   // event at the element.  It will take care of retargeting it to its
   // command element, if applicable, and executing the event handler.
   if (isXULKey) {
     return DispatchXULKeyCommand(aEvent);
   }
 
   // Look for a compiled handler on the element. 
   // Should be compiled and bound with "on" in front of the name.
-  nsCOMPtr<nsIAtom> onEventAtom = do_GetAtom(NS_LITERAL_STRING("onxbl") +
+  nsCOMPtr<nsIAtom> onEventAtom = NS_Atomize(NS_LITERAL_STRING("onxbl") +
                                              nsDependentAtomString(mEventName));
 
   // Compile the handler and bind it to the element.
   nsCOMPtr<nsIScriptGlobalObject> boundGlobal;
   nsCOMPtr<nsPIWindowRoot> winRoot = do_QueryInterface(aTarget);
   if (winRoot) {
     if (nsCOMPtr<nsPIDOMWindowOuter> window = winRoot->GetWindow()) {
       nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
@@ -761,17 +761,17 @@ nsXBLPrototypeHandler::ConstructPrototyp
   nsAutoString event(aEvent);
   if (event.IsEmpty()) {
     if (mType & NS_HANDLER_TYPE_XUL)
       GetEventType(event);
     if (event.IsEmpty())
       return;
   }
 
-  mEventName = do_GetAtom(event);
+  mEventName = NS_Atomize(event);
 
   if (aPhase) {
     const nsDependentString phase(aPhase);
     if (phase.EqualsLiteral("capturing"))
       mPhase = NS_PHASE_CAPTURING;
     else if (phase.EqualsLiteral("target"))
       mPhase = NS_PHASE_TARGET;
   }
@@ -955,17 +955,17 @@ nsXBLPrototypeHandler::Read(nsIObjectInp
   uint32_t detail; 
   rv = aStream->Read32(&detail);
   NS_ENSURE_SUCCESS(rv, rv);
   mDetail = detail;
 
   nsAutoString name;
   rv = aStream->ReadString(name);
   NS_ENSURE_SUCCESS(rv, rv);
-  mEventName = do_GetAtom(name);
+  mEventName = NS_Atomize(name);
 
   rv = aStream->Read32(&mLineNumber);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString handlerText;
   rv = aStream->ReadString(handlerText);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!handlerText.IsEmpty())
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -388,17 +388,17 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDO
     } else {
       HandleEventOnCaptureInDefaultEventGroup(keyEvent);
     }
     return NS_OK;
   }
 
   nsAutoString eventType;
   aEvent->GetType(eventType);
-  nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
+  nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
   NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
 
   return WalkHandlers(keyEvent, eventTypeAtom);
 }
 
 void
 nsXBLWindowKeyHandler::HandleEventOnCaptureInDefaultEventGroup(
                          nsIDOMKeyEvent* aEvent)
@@ -690,17 +690,17 @@ nsXBLWindowKeyHandler::HasHandlerForEven
   bool isDisabled;
   nsCOMPtr<Element> el = GetElement(&isDisabled);
   if (el && isDisabled) {
     return false;
   }
 
   nsAutoString eventType;
   aEvent->AsEvent()->GetType(eventType);
-  nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
+  nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
   NS_ENSURE_TRUE(eventTypeAtom, false);
 
   return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false,
                               aOutReservedForChrome);
 }
 
 already_AddRefed<Element>
 nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
--- a/dom/xml/ProcessingInstruction.cpp
+++ b/dom/xml/ProcessingInstruction.cpp
@@ -17,17 +17,17 @@ NS_NewXMLProcessingInstruction(nsNodeInf
                                const nsAString& aTarget,
                                const nsAString& aData)
 {
   using mozilla::dom::ProcessingInstruction;
   using mozilla::dom::XMLStylesheetProcessingInstruction;
 
   NS_PRECONDITION(aNodeInfoManager, "Missing nodeinfo manager");
 
-  nsCOMPtr<nsIAtom> target = do_GetAtom(aTarget);
+  nsCOMPtr<nsIAtom> target = NS_Atomize(aTarget);
   MOZ_ASSERT(target);
 
   if (target == nsGkAtoms::xml_stylesheet) {
     RefPtr<XMLStylesheetProcessingInstruction> pi =
       new XMLStylesheetProcessingInstruction(aNodeInfoManager, aData);
     return pi.forget();
   }
 
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1129,17 +1129,17 @@ nsXMLContentSink::HandleDoctypeDecl(cons
                                     nsISupports* aCatalogData)
 {
   FlushText();
 
   nsresult rv = NS_OK;
 
   NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment");
 
-  nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
+  nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
   NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a new doctype node
   nsCOMPtr<nsIDOMDocumentType> docType;
   rv = NS_NewDOMDocumentType(getter_AddRefs(docType), mNodeInfoManager,
                              name, aPublicId, aSystemId, aSubset);
   if (NS_FAILED(rv) || !docType) {
     return rv;
--- a/dom/xslt/base/txExpandedName.cpp
+++ b/dom/xslt/base/txExpandedName.cpp
@@ -17,25 +17,25 @@ txExpandedName::init(const nsAString& aQ
     const nsAFlatString& qName = PromiseFlatString(aQName);
     const char16_t* colon;
     bool valid = XMLUtils::isValidQName(qName, &colon);
     if (!valid) {
         return NS_ERROR_FAILURE;
     }
 
     if (colon) {
-        nsCOMPtr<nsIAtom> prefix = do_GetAtom(Substring(qName.get(), colon));
+        nsCOMPtr<nsIAtom> prefix = NS_Atomize(Substring(qName.get(), colon));
         int32_t namespaceID = aResolver->lookupNamespace(prefix);
         if (namespaceID == kNameSpaceID_Unknown)
             return NS_ERROR_FAILURE;
         mNamespaceID = namespaceID;
 
         const char16_t *end;
         qName.EndReading(end);
-        mLocalName = do_GetAtom(Substring(colon + 1, end));
+        mLocalName = NS_Atomize(Substring(colon + 1, end));
     }
     else {
         mNamespaceID = aUseDefault ? aResolver->lookupNamespace(nullptr) :
                                      kNameSpaceID_None;
-        mLocalName = do_GetAtom(aQName);
+        mLocalName = NS_Atomize(aQName);
     }
     return NS_OK;
 }
--- a/dom/xslt/base/txNamespaceMap.cpp
+++ b/dom/xslt/base/txNamespaceMap.cpp
@@ -84,15 +84,15 @@ txNamespaceMap::lookupNamespace(nsIAtom*
     }
 
     return kNameSpaceID_Unknown;
 }
 
 int32_t
 txNamespaceMap::lookupNamespaceWithDefault(const nsAString& aPrefix)
 {
-    nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
+    nsCOMPtr<nsIAtom> prefix = NS_Atomize(aPrefix);
     if (prefix != nsGkAtoms::_poundDefault) {
         return lookupNamespace(prefix);
     }
 
     return lookupNamespace(nullptr);
 }
--- a/dom/xslt/base/txStringUtils.h
+++ b/dom/xslt/base/txStringUtils.h
@@ -23,12 +23,12 @@ TX_StringEqualsAtom(const nsASingleFragm
 }
 
 inline already_AddRefed<nsIAtom>
 TX_ToLowerCaseAtom(nsIAtom* aAtom)
 {
   nsAutoString str;
   aAtom->ToString(str);
   nsContentUtils::ASCIIToLower(str);
-  return do_GetAtom(str);
+  return NS_Atomize(str);
 }
 
 #endif // txStringUtils_h__
--- a/dom/xslt/xml/txXMLUtils.cpp
+++ b/dom/xslt/xml/txXMLUtils.cpp
@@ -53,34 +53,34 @@ XMLUtils::splitExpatName(const char16_t 
                                                                     uriEnd));
         if (*aNameSpaceID == kNameSpaceID_Unknown) {
             return NS_ERROR_FAILURE;
         }
 
         nameStart = (uriEnd + 1);
         if (nameEnd)  {
             const char16_t *prefixStart = nameEnd + 1;
-            *aPrefix = NS_NewAtom(Substring(prefixStart, pos)).take();
+            *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take();
             if (!*aPrefix) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
         }
         else {
             nameEnd = pos;
             *aPrefix = nullptr;
         }
     }
     else {
         *aNameSpaceID = kNameSpaceID_None;
         nameStart = aExpatName;
         nameEnd = pos;
         *aPrefix = nullptr;
     }
 
-    *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd)).take();
+    *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take();
 
     return *aLocalName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 nsresult
 XMLUtils::splitQName(const nsAString& aName, nsIAtom** aPrefix,
                      nsIAtom** aLocalName)
 {
@@ -90,22 +90,22 @@ XMLUtils::splitQName(const nsAString& aN
     if (!valid) {
         return NS_ERROR_FAILURE;
     }
 
     if (colon) {
         const char16_t *end;
         qName.EndReading(end);
 
-        *aPrefix = NS_NewAtom(Substring(qName.get(), colon)).take();
-        *aLocalName = NS_NewAtom(Substring(colon + 1, end)).take();
+        *aPrefix = NS_Atomize(Substring(qName.get(), colon)).take();
+        *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
     }
     else {
         *aPrefix = nullptr;
-        *aLocalName = NS_NewAtom(aName).take();
+        *aLocalName = NS_Atomize(aName).take();
     }
 
     return NS_OK;
 }
 
 /**
  * Returns true if the given string has only whitespace characters
  */
--- a/dom/xslt/xpath/txExpr.h
+++ b/dom/xslt/xpath/txExpr.h
@@ -473,17 +473,17 @@ public:
     {
     }
 
     /*
      * Sets the name of the node to match. Only availible for pi nodes
      */
     void setNodeName(const nsAString& aName)
     {
-        mNodeName = do_GetAtom(aName);
+        mNodeName = NS_Atomize(aName);
     }
 
     NodeType getNodeTestType()
     {
         return mNodeType;
     }
 
     NodeTestType getType() override;
--- a/dom/xslt/xpath/txExprParser.cpp
+++ b/dom/xslt/xpath/txExprParser.cpp
@@ -477,17 +477,17 @@ txExprParser::createLocationStep(txExprL
 
     //-- get Axis Identifier or AbbreviatedStep, if present
     Token* tok = lexer.peek();
     switch (tok->mType) {
         case Token::AXIS_IDENTIFIER:
         {
             //-- eat token
             lexer.nextToken();
-            nsCOMPtr<nsIAtom> axis = do_GetAtom(tok->Value());
+            nsCOMPtr<nsIAtom> axis = NS_Atomize(tok->Value());
             if (axis == nsGkAtoms::ancestor) {
                 axisIdentifier = LocationStep::ANCESTOR_AXIS;
             }
             else if (axis == nsGkAtoms::ancestorOrSelf) {
                 axisIdentifier = LocationStep::ANCESTOR_OR_SELF_AXIS;
             }
             else if (axis == nsGkAtoms::attribute) {
                 axisIdentifier = LocationStep::ATTRIBUTE_AXIS;
@@ -889,35 +889,35 @@ nsresult
 txExprParser::resolveQName(const nsAString& aQName,
                            nsIAtom** aPrefix, txIParseContext* aContext,
                            nsIAtom** aLocalName, int32_t& aNamespace,
                            bool aIsNameTest)
 {
     aNamespace = kNameSpaceID_None;
     int32_t idx = aQName.FindChar(':');
     if (idx > 0) {
-        *aPrefix = NS_NewAtom(StringHead(aQName, (uint32_t)idx)).take();
+        *aPrefix = NS_Atomize(StringHead(aQName, (uint32_t)idx)).take();
         if (!*aPrefix) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
-        *aLocalName = NS_NewAtom(Substring(aQName, (uint32_t)idx + 1,
+        *aLocalName = NS_Atomize(Substring(aQName, (uint32_t)idx + 1,
                                            aQName.Length() - (idx + 1))).take();
         if (!*aLocalName) {
             NS_RELEASE(*aPrefix);
             return NS_ERROR_OUT_OF_MEMORY;
         }
         return aContext->resolveNamespacePrefix(*aPrefix, aNamespace);
     }
     // the lexer dealt with idx == 0
     *aPrefix = 0;
     if (aIsNameTest && aContext->caseInsensitiveNameTests()) {
         nsAutoString lcname;
         nsContentUtils::ASCIIToLower(aQName, lcname);
-        *aLocalName = NS_NewAtom(lcname).take();
+        *aLocalName = NS_Atomize(lcname).take();
     }
     else {
-        *aLocalName = NS_NewAtom(aQName).take();
+        *aLocalName = NS_Atomize(aQName).take();
     }
     if (!*aLocalName) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
     return NS_OK;
 }
--- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
+++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
@@ -353,17 +353,17 @@ txXPathNodeUtils::getLocalName(const txX
             return localName.forget();
         }
 
         if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
             nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
             nsAutoString target;
             node->GetNodeName(target);
 
-            return NS_NewAtom(target);
+            return NS_Atomize(target);
         }
 
         return nullptr;
     }
 
     nsCOMPtr<nsIAtom> localName = aNode.Content()->
         GetAttrNameAt(aNode.mIndex)->LocalName();
 
--- a/dom/xslt/xslt/txInstructions.cpp
+++ b/dom/xslt/xslt/txInstructions.cpp
@@ -109,17 +109,17 @@ txAttribute::execute(txExecutionState& a
     if (!XMLUtils::isValidQName(name, &colon) ||
         TX_StringEqualsAtom(name, nsGkAtoms::xmlns)) {
         return NS_OK;
     }
 
     nsCOMPtr<nsIAtom> prefix;
     uint32_t lnameStart = 0;
     if (colon) {
-        prefix = do_GetAtom(Substring(name.get(), colon));
+        prefix = NS_Atomize(Substring(name.get(), colon));
         lnameStart = colon - name.get() + 1;
     }
 
     int32_t nsId = kNameSpaceID_None;
     if (mNamespace) {
         nsAutoString nspace;
         rv = mNamespace->evaluateToString(aEs.getEvalContext(),
                                           nspace);
@@ -825,17 +825,17 @@ txStartElement::execute(txExecutionState
 
     int32_t nsId = kNameSpaceID_None;
     nsCOMPtr<nsIAtom> prefix;
     uint32_t lnameStart = 0;
 
     const char16_t* colon;
     if (XMLUtils::isValidQName(name, &colon)) {
         if (colon) {
-            prefix = do_GetAtom(Substring(name.get(), colon));
+            prefix = NS_Atomize(Substring(name.get(), colon));
             lnameStart = colon - name.get() + 1;
         }
 
         if (mNamespace) {
             nsAutoString nspace;
             rv = mNamespace->evaluateToString(aEs.getEvalContext(),
                                               nspace);
             NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/xslt/xslt/txMozillaXMLOutput.cpp
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -127,20 +127,20 @@ txMozillaXMLOutput::attribute(nsIAtom* a
                               const int32_t aNsID,
                               const nsString& aValue)
 {
     nsCOMPtr<nsIAtom> lname;
 
     if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
         nsAutoString lnameStr;
         nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
-        lname = do_GetAtom(lnameStr);
+        lname = NS_Atomize(lnameStr);
     }
     else {
-        lname = do_GetAtom(aLocalName);
+        lname = NS_Atomize(aLocalName);
     }
 
     NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
 
     // Check that it's a valid name
     if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
         // Try without prefix
         aPrefix = nullptr;
@@ -457,20 +457,20 @@ txMozillaXMLOutput::startElement(nsIAtom
     int32_t nsId = aNsID;
     nsCOMPtr<nsIAtom> lname;
 
     if (mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) {
         nsId = kNameSpaceID_XHTML;
 
         nsAutoString lnameStr;
         nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
-        lname = do_GetAtom(lnameStr);
+        lname = NS_Atomize(lnameStr);
     }
     else {
-        lname = do_GetAtom(aLocalName);
+        lname = NS_Atomize(aLocalName);
     }
 
     // No biggie if we lose the prefix due to OOM
     NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
 
     // Check that it's a valid name
     if (!nsContentUtils::IsValidNodeName(lname, aPrefix, nsId)) {
         // Try without prefix
@@ -751,17 +751,17 @@ txMozillaXMLOutput::endHTMLElement(nsICo
         // handle HTTP-EQUIV data
         nsAutoString httpEquiv;
         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, httpEquiv);
         if (!httpEquiv.IsEmpty()) {
             nsAutoString value;
             aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value);
             if (!value.IsEmpty()) {
                 nsContentUtils::ASCIIToLower(httpEquiv);
-                nsCOMPtr<nsIAtom> header = do_GetAtom(httpEquiv);
+                nsCOMPtr<nsIAtom> header = NS_Atomize(httpEquiv);
                 processHTTPEquiv(header, value);
             }
         }
     }
     
     return NS_OK;
 }
 
@@ -883,17 +883,17 @@ txMozillaXMLOutput::createResultDocument
         else {
             qName.Assign(aName);
         }
 
         nsCOMPtr<nsIDOMDocumentType> documentType;
 
         nsresult rv = nsContentUtils::CheckQName(qName);
         if (NS_SUCCEEDED(rv)) {
-            nsCOMPtr<nsIAtom> doctypeName = do_GetAtom(qName);
+            nsCOMPtr<nsIAtom> doctypeName = NS_Atomize(qName);
             if (!doctypeName) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
 
             // Indicate that there is no internal subset (not just an empty one)
             rv = NS_NewDOMDocumentType(getter_AddRefs(documentType),
                                        mNodeInfoManager,
                                        doctypeName,
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -391,17 +391,17 @@ txMozillaXSLTProcessor::SetSourceContent
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix,
                                               const nsString& aNamespace)
 {
-    nsCOMPtr<nsIAtom> pre = do_GetAtom(aPrefix);
+    nsCOMPtr<nsIAtom> pre = NS_Atomize(aPrefix);
     return mParamNamespaceMap.mapNamespace(pre, aNamespace);
 }
 
 
 class txXSLTParamContext : public txIParseContext,
                            public txIEvalContext
 {
 public:
@@ -513,17 +513,17 @@ txMozillaXSLTProcessor::AddXSLTParam(con
         // Evaluate
         rv = expr->evaluate(&paramContext, getter_AddRefs(value));
         NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
         value = new StringResult(aValue, nullptr);
     }
 
-    nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
+    nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
     int32_t nsId = kNameSpaceID_Unknown;
     rv = nsContentUtils::NameSpaceManager()->
         RegisterNameSpace(aNamespace, nsId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     txExpandedName varName(nsId, name);
     txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
     if (var) {
@@ -935,17 +935,17 @@ txMozillaXSLTProcessor::SetParameter(con
             return NS_ERROR_FAILURE;
         }        
     }
 
     int32_t nsId = kNameSpaceID_Unknown;
     nsresult rv = nsContentUtils::NameSpaceManager()->
         RegisterNameSpace(aNamespaceURI, nsId);
     NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIAtom> localName = do_GetAtom(aLocalName);
+    nsCOMPtr<nsIAtom> localName = NS_Atomize(aLocalName);
     txExpandedName varName(nsId, localName);
 
     txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
     if (var) {
         var->setValue(value);
         return NS_OK;
     }
 
@@ -957,17 +957,17 @@ NS_IMETHODIMP
 txMozillaXSLTProcessor::GetParameter(const nsAString& aNamespaceURI,
                                      const nsAString& aLocalName,