Merge m-c to fx-team
authorPhil Ringnalda <philringnalda@gmail.com>
Tue, 26 Feb 2013 14:32:37 -0800
changeset 123007 ad4cc4e97774fd21f223ad681297d1a2cdd9ff37
parent 123006 fb9a02b79254b74ad1221133fcbfdda9f8ea5703 (current diff)
parent 122990 55d8934477f6e323aed914720aed703e43f6d85a (diff)
child 123008 eb562a97db7c9b4a29b2997d7fa32c67bfbf135e
push id24369
push userttaubert@mozilla.com
push dateWed, 27 Feb 2013 00:00:58 +0000
treeherdermozilla-central@ad4cc4e97774 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team
browser/components/privatebrowsing/src/Makefile.in
browser/components/privatebrowsing/src/nsPrivateBrowsingServiceObsolete.js
browser/components/privatebrowsing/src/nsPrivateBrowsingServiceObsolete.manifest
browser/metro/shell/VisualElementsManifest.xml.in
content/html/content/src/nsHTMLIFrameElement.cpp
content/html/content/src/nsHTMLIFrameElement.h
dom/base/nsDOMWindowUtils.cpp
js/xpconnect/tests/mochitest/test_bug553407.html
netwerk/base/public/nsIPrivateBrowsingServiceObsolete.idl
--- a/CLOBBER
+++ b/CLOBBER
@@ -10,9 +10,9 @@
 #                  O   <-- Users coming from both parents need to Clobber
 #               /     \
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
-Bug 829832 - BackgroundFileSaver xpcshell failures without clobber
+Bug 845063 needs clobber
--- a/accessible/public/nsIAccessibleRelation.idl
+++ b/accessible/public/nsIAccessibleRelation.idl
@@ -5,119 +5,114 @@
 
 #include "nsISupports.idl"
 #include "nsIArray.idl"
 
 interface nsIAccessible;
 
 /**
  * This interface gives access to an accessible's set of relations.
- * Be carefull, do not change constants until ATK has a structure to map gecko
- * constants into ATK constants.
  */
-[scriptable, uuid(f42a1589-70ab-4704-877f-4a9162bbe188)]
+[scriptable, uuid(9f85fc0d-2969-48e6-b822-68140f7e5770)]
 interface nsIAccessibleRelation : nsISupports
 {
+  /**
+   * This object is labelled by a target object.
+   */
+  const unsigned long RELATION_LABELLED_BY = 0x00;
 
-  const unsigned long RELATION_NUL = 0x00;
+  /**
+   * This object is label for a target object.
+   */
+  const unsigned long RELATION_LABEL_FOR = 0x01;
+
+  /**
+   * This object is described by the target object.
+   */
+  const unsigned long RELATION_DESCRIBED_BY = 0x02;
+
+  /**
+   * This object is describes the target object.
+   */
+  const unsigned long RELATION_DESCRIPTION_FOR = 0x3;
+
+  /**
+   * This object is a child of a target object.
+   */
+  const unsigned long RELATION_NODE_CHILD_OF = 0x4;
+
+  /**
+   * This object is a parent of a target object. A dual relation to
+   * RELATION_NODE_CHILD_OF
+   */
+  const unsigned long RELATION_NODE_PARENT_OF = 0x5;
 
   /**
    * Some attribute of this object is affected by a target object.
    */
-  const unsigned long RELATION_CONTROLLED_BY = 0x01;
-
-  // First relation
-  const unsigned long RELATION_FIRST = RELATION_CONTROLLED_BY;
+  const unsigned long RELATION_CONTROLLED_BY = 0x06;
 
   /**
    * This object is interactive and controls some attribute of a target object.
    */
-  const unsigned long RELATION_CONTROLLER_FOR = 0x02;
+  const unsigned long RELATION_CONTROLLER_FOR = 0x07;
 
   /**
-   * This object is label for a target object.
+   * Content flows from this object to a target object, i.e. has content that
+   * flows logically to another object in a sequential way, e.g. text flow.
    */
-  const unsigned long RELATION_LABEL_FOR = 0x03;
+  const unsigned long RELATION_FLOWS_TO = 0x08;
 
   /**
-   * This object is labelled by a target object.
+   * Content flows to this object from a target object, i.e. has content that
+   * flows logically from another object in a sequential way, e.g. text flow.
    */
-  const unsigned long RELATION_LABELLED_BY = 0x04;
+  const unsigned long RELATION_FLOWS_FROM = 0x09;
 
   /**
    * This object is a member of a group of one or more objects. When there is
    * more than one object in the group each member may have one and the same
    * target, e.g. a grouping object.  It is also possible that each member has
    * multiple additional targets, e.g. one for every other member in the group.
    */
-  const unsigned long RELATION_MEMBER_OF = 0x05;
-
-  /**
-   * This object is a child of a target object.
-   */
-  const unsigned long RELATION_NODE_CHILD_OF = 0x06;
-
-  /**
-   * Content flows from this object to a target object, i.e. has content that
-   * flows logically to another object in a sequential way, e.g. text flow.
-   */
-  const unsigned long RELATION_FLOWS_TO = 0x07;
-
-  /**
-   * Content flows to this object from a target object, i.e. has content that
-   * flows logically from another object in a sequential way, e.g. text flow.
-   */
-  const unsigned long RELATION_FLOWS_FROM = 0x08;
+  const unsigned long RELATION_MEMBER_OF = 0x0a;
 
   /**
    * This object is a sub window of a target object.
    */
-  const unsigned long RELATION_SUBWINDOW_OF = 0x09;
+  const unsigned long RELATION_SUBWINDOW_OF = 0x0b;
 
   /**
    * This object embeds a target object. This relation can be used on the
    * OBJID_CLIENT accessible for a top level window to show where the content
    * areas are.
    */
-  const unsigned long RELATION_EMBEDS = 0x0a;
+  const unsigned long RELATION_EMBEDS = 0x0c;
 
   /**
    * This object is embedded by a target object.
    */
-  const unsigned long RELATION_EMBEDDED_BY = 0x0b;
+  const unsigned long RELATION_EMBEDDED_BY = 0x0d;
 
   /**
    * This object is a transient component related to the target object. When
    * this object is activated the target object doesn't lose focus.
    */
-  const unsigned long RELATION_POPUP_FOR = 0x0c;
+  const unsigned long RELATION_POPUP_FOR = 0x0e;
 
   /**
    * This object is a parent window of the target object.
    */
-  const unsigned long RELATION_PARENT_WINDOW_OF = 0x0d;
-
-  /**
-   * This object is described by the target object.
-   */
-  const unsigned long RELATION_DESCRIBED_BY = 0x0e;
-
-  /**
-   * This object is describes the target object.
-   */
-  const unsigned long RELATION_DESCRIPTION_FOR = 0x0f;
-
-  // Last relation that is standard to desktop accessibility APIs
-  const unsigned long RELATION_LAST = RELATION_DESCRIPTION_FOR;
+  const unsigned long RELATION_PARENT_WINDOW_OF = 0x0f;
 
   /**
    * Part of a form/dialog with a related default button. It is used for
-   * MSAA only, no for IA2 nor ATK.
+   * MSAA/XPCOM, it isn't for IA2 or ATK.
    */
-  const unsigned long RELATION_DEFAULT_BUTTON = 0x4000;
+  const unsigned long RELATION_DEFAULT_BUTTON = 0x10;
 
   /**
    * Returns the type of the relation.
    */
   readonly attribute unsigned long relationType;
 
   /**
    * Returns the number of targets for this relation.
--- a/accessible/src/atk/AccessibleWrap.cpp
+++ b/accessible/src/atk/AccessibleWrap.cpp
@@ -848,31 +848,39 @@ refRelationSetCB(AtkObject *aAtkObj)
 {
   AtkRelationSet* relation_set =
     ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
 
   AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   if (!accWrap)
     return relation_set;
 
-  uint32_t relationTypes[] = {
-    nsIAccessibleRelation::RELATION_LABELLED_BY,
-    nsIAccessibleRelation::RELATION_LABEL_FOR,
-    nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
+  // Keep in sync with AtkRelationType enum.
+  static const uint32_t relationTypes[] = {
     nsIAccessibleRelation::RELATION_CONTROLLED_BY,
     nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
-    nsIAccessibleRelation::RELATION_EMBEDS,
+    nsIAccessibleRelation::RELATION_LABEL_FOR,
+    nsIAccessibleRelation::RELATION_LABELLED_BY,
+    nsIAccessibleRelation::RELATION_MEMBER_OF,
+    nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
     nsIAccessibleRelation::RELATION_FLOWS_TO,
     nsIAccessibleRelation::RELATION_FLOWS_FROM,
+    nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
+    nsIAccessibleRelation::RELATION_EMBEDS,
+    nsIAccessibleRelation::RELATION_EMBEDDED_BY,
+    nsIAccessibleRelation::RELATION_POPUP_FOR,
+    nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
     nsIAccessibleRelation::RELATION_DESCRIBED_BY,
     nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
+    nsIAccessibleRelation::RELATION_NODE_PARENT_OF
   };
 
   for (uint32_t i = 0; i < ArrayLength(relationTypes); i++) {
-    AtkRelationType atkType = static_cast<AtkRelationType>(relationTypes[i]);
+    // Shift to 1 to skip ATK_RELATION_NULL.
+    AtkRelationType atkType = static_cast<AtkRelationType>(i + 1);
     AtkRelation* atkRelation =
       atk_relation_set_get_relation_by_type(relation_set, atkType);
     if (atkRelation)
       atk_relation_set_remove(relation_set, atkRelation);
 
     Relation rel(accWrap->RelationByType(relationTypes[i]));
     nsTArray<AtkObject*> targets;
     Accessible* tempAcc = nullptr;
--- a/accessible/src/base/AccGroupInfo.cpp
+++ b/accessible/src/base/AccGroupInfo.cpp
@@ -133,16 +133,65 @@ AccGroupInfo::AccGroupInfo(Accessible* a
   }
 
   // Previous sibling of parent group is a tree item, this is the
   // conceptual tree item parent.
   if (parentPrevSiblingRole == roles::OUTLINEITEM)
     mParent = parentPrevSibling;
 }
 
+Accessible*
+AccGroupInfo::FirstItemOf(Accessible* aContainer)
+{
+  // ARIA trees can be arranged by ARIA groups, otherwise aria-level works.
+  a11y::role containerRole = aContainer->Role();
+  Accessible* item = aContainer->NextSibling();
+  if (item) {
+    if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
+      item = item->FirstChild();
+
+    AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
+    if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
+      return item;
+  }
+
+  // Otherwise it can be a direct child.
+  item = aContainer->FirstChild();
+  if (item && IsConceptualParent(BaseRole(item->Role()), containerRole))
+    return item;
+
+  return nullptr;
+}
+
+Accessible*
+AccGroupInfo::NextItemTo(Accessible* aItem)
+{
+  AccGroupInfo* groupInfo = aItem->GetGroupInfo();
+  if (!groupInfo)
+    return nullptr;
+
+  // If the item in middle of the group then search next item in siblings.
+  if (groupInfo->PosInSet() >= groupInfo->SetSize())
+    return nullptr;
+
+  Accessible* parent = aItem->Parent();
+  uint32_t childCount = parent->ChildCount();
+  for (int32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
+    Accessible* nextItem = parent->GetChildAt(idx);
+    AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
+    if (nextGroupInfo &&
+        nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
+      return nextItem;
+    }
+  }
+
+  NS_NOTREACHED("Item in the midle of the group but there's no next item!");
+  return nullptr;
+}
+
 bool
 AccGroupInfo::IsConceptualParent(role aRole, role aParentRole)
 {
   if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
     return true;
   if ((aParentRole == roles::TABLE || aParentRole == roles::TREE_TABLE) &&
       aRole == roles::ROW)
     return true;
--- a/accessible/src/base/AccGroupInfo.h
+++ b/accessible/src/base/AccGroupInfo.h
@@ -12,21 +12,32 @@ namespace mozilla {
 namespace a11y {
 
 /**
  * Calculate and store group information.
  */
 class AccGroupInfo
 {
 public:
-  AccGroupInfo(Accessible* aItem, mozilla::a11y::role aRole);
   ~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
 
-  int32_t PosInSet() const { return mPosInSet; }
+  /**
+   * Return 1-based position in the group.
+   */
+  uint32_t PosInSet() const { return mPosInSet; }
+
+  /**
+   * Return a number of items in the group.
+   */
   uint32_t SetSize() const { return mSetSize; }
+
+  /**
+   * Return a direct or logical parent of the accessible that this group info is
+   * created for.
+   */
   Accessible* ConceptualParent() const { return mParent; }
 
   /**
    * Create group info.
    */
   static AccGroupInfo* CreateGroupInfo(Accessible* aAccessible)
   {
     mozilla::a11y::role role = aAccessible->Role();
@@ -45,19 +56,33 @@ public:
         role != mozilla::a11y::roles::RADIOBUTTON &&
         role != mozilla::a11y::roles::PAGETAB)
       return nullptr;
 
     AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
     return info;
   }
 
+  /**
+   * Return a first item for the given container.
+   */
+  static Accessible* FirstItemOf(Accessible* aContainer);
+
+  /**
+   * Return next item of the same group to the given item.
+   */
+  static Accessible* NextItemTo(Accessible* aItem);
+
+protected:
+  AccGroupInfo(Accessible* aItem, a11y::role aRole);
+
 private:
-  AccGroupInfo(const AccGroupInfo&);
-  AccGroupInfo& operator =(const AccGroupInfo&);
+  AccGroupInfo() MOZ_DELETE;
+  AccGroupInfo(const AccGroupInfo&) MOZ_DELETE;
+  AccGroupInfo& operator =(const AccGroupInfo&) MOZ_DELETE;
 
   static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
   {
     if (aRole == mozilla::a11y::roles::CHECK_MENU_ITEM ||
         aRole == mozilla::a11y::roles::PARENT_MENUITEM ||
         aRole == mozilla::a11y::roles::RADIO_MENU_ITEM)
       return mozilla::a11y::roles::MENUITEM;
 
@@ -66,18 +91,17 @@ private:
 
     return aRole;
   }
 
   /**
    * Return true if the given parent role is conceptual parent of the given
    * role.
    */
-  static bool IsConceptualParent(mozilla::a11y::role aRole,
-				 mozilla::a11y::role aParentRole);
+  static bool IsConceptualParent(a11y::role aRole, a11y::role aParentRole);
 
   uint32_t mPosInSet;
   uint32_t mSetSize;
   Accessible* mParent;
 };
 
 } // namespace mozilla
 } // namespace a11y
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -1,16 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccIterator.h"
 
 #include "nsAccessibilityService.h"
+#include "AccGroupInfo.h"
 #include "Accessible-inl.h"
+#ifdef MOZ_XUL
+#include "XULTreeAccessible.h"
+#endif
 
 #include "mozilla/dom/Element.h"
 #include "nsBindingManager.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -324,16 +328,74 @@ IDRefsIterator::GetElem(const nsDependen
 
 Accessible*
 IDRefsIterator::Next()
 {
   nsIContent* nextElm = NextElem();
   return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
 }
 
+
+////////////////////////////////////////////////////////////////////////////////
+// SingleAccIterator
+////////////////////////////////////////////////////////////////////////////////
+
 Accessible*
 SingleAccIterator::Next()
 {
   nsRefPtr<Accessible> nextAcc;
   mAcc.swap(nextAcc);
   return (nextAcc && !nextAcc->IsDefunct()) ? nextAcc : nullptr;
 }
 
+
+////////////////////////////////////////////////////////////////////////////////
+// ItemIterator
+////////////////////////////////////////////////////////////////////////////////
+
+Accessible*
+ItemIterator::Next()
+{
+  if (mContainer) {
+    mAnchor = AccGroupInfo::FirstItemOf(mContainer);
+    mContainer = nullptr;
+    return mAnchor;
+  }
+
+  return mAnchor ? (mAnchor = AccGroupInfo::NextItemTo(mAnchor)) : nullptr;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemIterator
+////////////////////////////////////////////////////////////////////////////////
+
+XULTreeItemIterator::XULTreeItemIterator(XULTreeAccessible* aXULTree,
+                                         nsITreeView* aTreeView,
+                                         int32_t aRowIdx) :
+  mXULTree(aXULTree), mTreeView(aTreeView), mRowCount(-1),
+  mContainerLevel(-1), mCurrRowIdx(aRowIdx + 1)
+{
+  mTreeView->GetRowCount(&mRowCount);
+  if (aRowIdx != -1)
+    mTreeView->GetLevel(aRowIdx, &mContainerLevel);
+}
+
+Accessible*
+XULTreeItemIterator::Next()
+{
+  while (mCurrRowIdx < mRowCount) {
+    int32_t level = 0;
+    mTreeView->GetLevel(mCurrRowIdx, &level);
+
+    if (level == mContainerLevel + 1)
+      return mXULTree->GetTreeItemAccessible(mCurrRowIdx++);
+
+    if (level <= mContainerLevel) { // got level up
+      mCurrRowIdx = mRowCount;
+      break;
+    }
+
+    mCurrRowIdx++;
+  }
+
+  return nullptr;
+}
--- a/accessible/src/base/AccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -261,12 +261,59 @@ public:
 private:
   SingleAccIterator();
   SingleAccIterator(const SingleAccIterator&);
   SingleAccIterator& operator = (const SingleAccIterator&);
 
   nsRefPtr<Accessible> mAcc;
 };
 
+
+/**
+ * Used to iterate items of the given item container.
+ */
+class ItemIterator : public AccIterable
+{
+public:
+  ItemIterator(Accessible* aItemContainer) :
+    mContainer(aItemContainer), mAnchor(nullptr) { }
+  virtual ~ItemIterator() { }
+
+  virtual Accessible* Next();
+
+private:
+  ItemIterator() MOZ_DELETE;
+  ItemIterator(const ItemIterator&) MOZ_DELETE;
+  ItemIterator& operator = (const ItemIterator&) MOZ_DELETE;
+
+  Accessible* mContainer;
+  Accessible* mAnchor;
+};
+
+
+/**
+ * Used to iterate through XUL tree items of the same level.
+ */
+class XULTreeItemIterator : public AccIterable
+{
+public:
+  XULTreeItemIterator(XULTreeAccessible* aXULTree, nsITreeView* aTreeView,
+                      int32_t aRowIdx);
+  virtual ~XULTreeItemIterator() { }
+
+  virtual Accessible* Next();
+
+private:
+  XULTreeItemIterator() MOZ_DELETE;
+  XULTreeItemIterator(const XULTreeItemIterator&) MOZ_DELETE;
+  XULTreeItemIterator& operator = (const XULTreeItemIterator&) MOZ_DELETE;
+
+  XULTreeAccessible* mXULTree;
+  nsITreeView* mTreeView;
+  int32_t mRowCount;
+  int32_t mContainerLevel;
+  int32_t mCurrRowIdx;
+};
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/base/Relation.h
+++ b/accessible/src/base/Relation.h
@@ -14,37 +14,36 @@ namespace a11y {
 
 /**
  * This class is used to return Relation objects from functions.  A copy
  * constructor doesn't work here because we need to mutate the old relation to
  * have its nsAutoPtr forget what it points to.
  */
 struct RelationCopyHelper
 {
-  RelationCopyHelper(mozilla::a11y::AccIterable* aFirstIter,
-                     mozilla::a11y::AccIterable* aLastIter) :
+  RelationCopyHelper(AccIterable* aFirstIter, AccIterable* aLastIter) :
     mFirstIter(aFirstIter), mLastIter(aLastIter) { }
 
-  mozilla::a11y::AccIterable* mFirstIter;
-  mozilla::a11y::AccIterable* mLastIter;
+  AccIterable* mFirstIter;
+  AccIterable* mLastIter;
 };
 
 /**
  * A collection of relation targets of a certain type.  Targets are computed
  * lazily while enumerating.
  */
 class Relation
 {
 public:
   Relation() : mFirstIter(nullptr), mLastIter(nullptr) { }
 
   Relation(const RelationCopyHelper aRelation) :
     mFirstIter(aRelation.mFirstIter), mLastIter(aRelation.mLastIter) { }
 
-  Relation(mozilla::a11y::AccIterable* aIter) :
+  Relation(AccIterable* aIter) :
     mFirstIter(aIter), mLastIter(aIter) { }
 
   Relation(Accessible* aAcc) :
     mFirstIter(nullptr), mLastIter(nullptr)
     { AppendTarget(aAcc); }
 
   Relation(DocAccessible* aDocument, nsIContent* aContent) :
     mFirstIter(nullptr), mLastIter(nullptr)
@@ -64,33 +63,33 @@ public:
     return *this;
   }
 
   operator RelationCopyHelper()
   {
     return RelationCopyHelper(mFirstIter.forget(), mLastIter);
   }
 
-  inline void AppendIter(mozilla::a11y::AccIterable* aIter)
+  inline void AppendIter(AccIterable* aIter)
   {
     if (mLastIter)
       mLastIter->mNextIter = aIter;
     else
       mFirstIter = aIter;
 
     mLastIter = aIter;
   }
 
   /**
    * Append the given accessible to the set of related accessibles.
    */
   inline void AppendTarget(Accessible* aAcc)
   {
     if (aAcc)
-      AppendIter(new mozilla::a11y::SingleAccIterator(aAcc));
+      AppendIter(new SingleAccIterator(aAcc));
   }
 
   /**
    * Append the one accessible for this content node to the set of related
    * accessibles.
    */
   void AppendTarget(DocAccessible* aDocument, nsIContent* aContent)
   {
@@ -113,17 +112,17 @@ public:
       mLastIter = nullptr;
 
     return target;
   }
 
 private:
   Relation& operator = (const Relation&);
 
-  nsAutoPtr<mozilla::a11y::AccIterable> mFirstIter;
-  mozilla::a11y::AccIterable* mLastIter;
+  nsAutoPtr<AccIterable> mFirstIter;
+  AccIterable* mLastIter;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/base/Role.h
+++ b/accessible/src/base/Role.h
@@ -582,19 +582,18 @@ enum Role {
 
   /**
    * A toggle button. A specialized push button that can be checked or
    * unchecked, but does not provide a separate indicator for the current state.
    */
   TOGGLE_BUTTON = 93,
 
   /**
-   * Representas a control that is capable of expanding and collapsing rows as
+   * Represent a control that is capable of expanding and collapsing rows as
    * well as showing multiple columns of data.
-   * XXX: it looks like this role is dupe of OUTLINE.
    */
   TREE_TABLE = 94,
 
   /**
    * A viewport. An object usually used in a scroll pane. It represents the
    * portion of the entire data that the user can see. As the user manipulates
    * the scroll bars, the contents of the viewport can change. Also refer to
    * SCROLL_PANE.
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -313,29 +313,29 @@ static const char kEventTypeNames[][40] 
   "virtual cursor changed"                   // EVENT_VIRTUALCURSOR_CHANGED
 };
 
 /**
  * Map nsIAccessibleRelation constants to strings. Used by
  * nsIAccessibleRetrieval::getStringRelationType() method.
  */
 static const char kRelationTypeNames[][20] = {
-  "unknown",             // RELATION_NUL
+  "labelled by",         // RELATION_LABELLED_BY
+  "label for",           // RELATION_LABEL_FOR
+  "described by",        // RELATION_DESCRIBED_BY
+  "description for",     // RELATION_DESCRIPTION_FOR
+  "node child of",       // RELATION_NODE_CHILD_OF
+  "node parent of",      // RELATION_NODE_PARENT_OF
   "controlled by",       // RELATION_CONTROLLED_BY
   "controller for",      // RELATION_CONTROLLER_FOR
-  "label for",           // RELATION_LABEL_FOR
-  "labelled by",         // RELATION_LABELLED_BY
-  "member of",           // RELATION_MEMBER_OF
-  "node child of",       // RELATION_NODE_CHILD_OF
   "flows to",            // RELATION_FLOWS_TO
   "flows from",          // RELATION_FLOWS_FROM
+  "member of",           // RELATION_MEMBER_OF
   "subwindow of",        // RELATION_SUBWINDOW_OF
   "embeds",              // RELATION_EMBEDS
   "embedded by",         // RELATION_EMBEDDED_BY
   "popup for",           // RELATION_POPUP_FOR
   "parent window of",    // RELATION_PARENT_WINDOW_OF
-  "described by",        // RELATION_DESCRIBED_BY
-  "description for",     // RELATION_DESCRIPTION_FOR
   "default button"       // RELATION_DEFAULT_BUTTON
 };
 
 #endif /* __nsIAccessibilityService_h__ */
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -1962,72 +1962,72 @@ Relation
 Accessible::RelationByType(uint32_t aType)
 {
   if (!HasOwnContent())
     return Relation();
 
   // Relationships are defined on the same content node that the role would be
   // defined on.
   switch (aType) {
-    case nsIAccessibleRelation::RELATION_LABEL_FOR: {
-      Relation rel(new RelatedAccIterator(Document(), mContent,
-                                          nsGkAtoms::aria_labelledby));
-      if (mContent->Tag() == nsGkAtoms::label)
-        rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
-                                          nsGkAtoms::_for :
-                                          nsGkAtoms::control));
-
-      return rel;
-    }
     case nsIAccessibleRelation::RELATION_LABELLED_BY: {
       Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_labelledby));
       if (mContent->IsHTML()) {
         rel.AppendIter(new HTMLLabelIterator(Document(), this));
       } else if (mContent->IsXUL()) {
         rel.AppendIter(new XULLabelIterator(Document(), mContent));
       }
 
       return rel;
     }
+
+    case nsIAccessibleRelation::RELATION_LABEL_FOR: {
+      Relation rel(new RelatedAccIterator(Document(), mContent,
+                                          nsGkAtoms::aria_labelledby));
+      if (mContent->Tag() == nsGkAtoms::label)
+        rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
+          nsGkAtoms::_for :
+          nsGkAtoms::control));
+
+      return rel;
+    }
+
     case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
       Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_describedby));
       if (mContent->IsXUL())
         rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
 
       return rel;
     }
+
     case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR: {
       Relation rel(new RelatedAccIterator(Document(), mContent,
                                           nsGkAtoms::aria_describedby));
 
       // This affectively adds an optional control attribute to xul:description,
       // which only affects accessibility, by allowing the description to be
       // tied to a control.
       if (mContent->Tag() == nsGkAtoms::description &&
           mContent->IsXUL())
         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
                                           nsGkAtoms::control));
 
       return rel;
     }
+
     case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
       Relation rel(new RelatedAccIterator(Document(), mContent,
                                           nsGkAtoms::aria_owns));
-      
+
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM || 
                             mRoleMapEntry->role == roles::ROW)) {
-        AccGroupInfo* groupInfo = GetGroupInfo();
-        if (!groupInfo)
-          return rel;
-
-        rel.AppendTarget(groupInfo->ConceptualParent());
+        rel.AppendTarget(GetGroupInfo()->ConceptualParent());
       }
 
       // If accessible is in its own Window, or is the root of a document,
       // then we should provide NODE_CHILD_OF relation so that MSAA clients
       // can easily get to true parent instead of getting to oleacc's
       // ROLE_WINDOW accessible which will prevent us from going up further
       // (because it is system generated and has no idea about the hierarchy
       // above it).
@@ -2038,31 +2038,62 @@ Accessible::RelationByType(uint32_t aTyp
           nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
           if (scrollFrame || view->GetWidget() || !frame->GetParent())
             rel.AppendTarget(Parent());
         }
       }
 
       return rel;
     }
+
+    case nsIAccessibleRelation::RELATION_NODE_PARENT_OF: {
+      Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
+
+      // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
+      // also can be organized by groups.
+      if (mRoleMapEntry &&
+          (mRoleMapEntry->role == roles::OUTLINEITEM ||
+           mRoleMapEntry->role == roles::ROW ||
+           mRoleMapEntry->role == roles::OUTLINE ||
+           mRoleMapEntry->role == roles::TREE_TABLE)) {
+        rel.AppendIter(new ItemIterator(this));
+      }
+
+      return rel;
+    }
+
     case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
       return Relation(new RelatedAccIterator(Document(), mContent,
                                              nsGkAtoms::aria_controls));
+
     case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
       Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_controls));
       rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
       return rel;
     }
+
     case nsIAccessibleRelation::RELATION_FLOWS_TO:
       return Relation(new IDRefsIterator(mDoc, mContent,
                                          nsGkAtoms::aria_flowto));
+
     case nsIAccessibleRelation::RELATION_FLOWS_FROM:
       return Relation(new RelatedAccIterator(Document(), mContent,
                                              nsGkAtoms::aria_flowto));
+
+    case nsIAccessibleRelation::RELATION_MEMBER_OF:
+          return Relation(mDoc, GetAtomicRegion());
+
+    case nsIAccessibleRelation::RELATION_SUBWINDOW_OF:
+    case nsIAccessibleRelation::RELATION_EMBEDS:
+    case nsIAccessibleRelation::RELATION_EMBEDDED_BY:
+    case nsIAccessibleRelation::RELATION_POPUP_FOR:
+    case nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF:
+      return Relation();
+
     case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON: {
       if (mContent->IsHTML()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
         if (control) {
           nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
           if (form) {
             nsCOMPtr<nsIContent> formContent =
@@ -2100,46 +2131,57 @@ Accessible::RelationByType(uint32_t aTyp
             }
           }
           nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
           return Relation(mDoc, relatedContent);
         }
       }
       return Relation();
     }
-    case nsIAccessibleRelation::RELATION_MEMBER_OF:
-      return Relation(mDoc, GetAtomicRegion());
-    case nsIAccessibleRelation::RELATION_SUBWINDOW_OF:
-    case nsIAccessibleRelation::RELATION_EMBEDS:
-    case nsIAccessibleRelation::RELATION_EMBEDDED_BY:
-    case nsIAccessibleRelation::RELATION_POPUP_FOR:
-    case nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF:
+
     default:
-    return Relation();
+      return Relation();
   }
 }
 
 NS_IMETHODIMP
 Accessible::GetRelations(nsIArray **aRelations)
 {
   NS_ENSURE_ARG_POINTER(aRelations);
   *aRelations = nullptr;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
   NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
 
-  for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
-       relType < nsIAccessibleRelation::RELATION_LAST;
-       ++relType) {
-
+  static const uint32_t relationTypes[] = {
+    nsIAccessibleRelation::RELATION_LABELLED_BY,
+    nsIAccessibleRelation::RELATION_LABEL_FOR,
+    nsIAccessibleRelation::RELATION_DESCRIBED_BY,
+    nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
+    nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
+    nsIAccessibleRelation::RELATION_NODE_PARENT_OF,
+    nsIAccessibleRelation::RELATION_CONTROLLED_BY,
+    nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
+    nsIAccessibleRelation::RELATION_FLOWS_TO,
+    nsIAccessibleRelation::RELATION_FLOWS_FROM,
+    nsIAccessibleRelation::RELATION_MEMBER_OF,
+    nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
+    nsIAccessibleRelation::RELATION_EMBEDS,
+    nsIAccessibleRelation::RELATION_EMBEDDED_BY,
+    nsIAccessibleRelation::RELATION_POPUP_FOR,
+    nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
+    nsIAccessibleRelation::RELATION_DEFAULT_BUTTON
+  };
+
+  for (uint32_t idx = 0; idx < ArrayLength(relationTypes); idx++) {
     nsCOMPtr<nsIAccessibleRelation> relation;
-    nsresult rv = GetRelationByType(relType, getter_AddRefs(relation));
+    nsresult rv = GetRelationByType(relationTypes[idx], getter_AddRefs(relation));
 
     if (NS_SUCCEEDED(rv) && relation) {
       uint32_t targets = 0;
       relation->GetTargetsCount(&targets);
       if (targets)
         relations->AppendElement(relation, false);
     }
   }
--- a/accessible/src/msaa/AccessibleWrap.cpp
+++ b/accessible/src/msaa/AccessibleWrap.cpp
@@ -8,17 +8,16 @@
 
 #include "Compatibility.h"
 #include "DocAccessible-inl.h"
 #include "EnumVariant.h"
 #include "ia2AccessibleRelation.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleEvent.h"
-#include "nsIAccessibleRelation.h"
 #include "nsWinUtils.h"
 #include "ServiceProvider.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "sdnAccessible.h"
 #include "States.h"
 
@@ -1043,19 +1042,18 @@ AccessibleWrap::get_nRelations(long *aNR
   if (!aNRelations)
     return E_INVALIDARG;
 
   *aNRelations = 0;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
-       relType <= nsIAccessibleRelation::RELATION_LAST; relType++) {
-    Relation rel = RelationByType(relType);
+  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
+    Relation rel = RelationByType(sRelationTypesForIA2[idx]);
     if (rel.Next())
       (*aNRelations)++;
   }
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -1069,18 +1067,18 @@ AccessibleWrap::get_relation(long aRelat
     return E_INVALIDARG;
 
   *aRelation = NULL;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   long relIdx = 0;
-  for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
-       relType <= nsIAccessibleRelation::RELATION_LAST; relType++) {
+  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
+    uint32_t relType = sRelationTypesForIA2[idx];
     Relation rel = RelationByType(relType);
     nsRefPtr<ia2AccessibleRelation> ia2Relation =
       new ia2AccessibleRelation(relType, &rel);
     if (ia2Relation->HasTargets()) {
       if (relIdx == aRelationIndex) {
         ia2Relation.forget(aRelation);
         return S_OK;
       }
@@ -1104,19 +1102,19 @@ AccessibleWrap::get_relations(long aMaxR
   if (!aRelation || !aNRelations)
     return E_INVALIDARG;
 
   *aNRelations = 0;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
-       relType <= nsIAccessibleRelation::RELATION_LAST &&
-       *aNRelations < aMaxRelations; relType++) {
+  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2) &&
+       *aNRelations < aMaxRelations; idx++) {
+    uint32_t relType = sRelationTypesForIA2[idx];
     Relation rel = RelationByType(relType);
     nsRefPtr<ia2AccessibleRelation> ia2Rel =
       new ia2AccessibleRelation(relType, &rel);
     if (ia2Rel->HasTargets()) {
       ia2Rel.forget(aRelation + (*aNRelations));
       (*aNRelations)++;
     }
   }
--- a/accessible/src/windows/ia2/ia2AccessibleRelation.h
+++ b/accessible/src/windows/ia2/ia2AccessibleRelation.h
@@ -4,16 +4,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/. */
 
 #ifndef _NS_ACCESSIBLE_RELATION_WRAP_H
 #define _NS_ACCESSIBLE_RELATION_WRAP_H
 
 #include "Accessible.h"
+#include "nsIAccessibleRelation.h"
 
 #include "nsTArray.h"
 
 #include "AccessibleRelation.h"
 
 namespace mozilla {
 namespace a11y {
 
@@ -55,13 +56,35 @@ private:
   ia2AccessibleRelation(const ia2AccessibleRelation&);
   ia2AccessibleRelation& operator = (const ia2AccessibleRelation&);
 
   uint32_t mType;
   nsTArray<nsRefPtr<Accessible> > mTargets;
   ULONG mReferences;
 };
 
+
+/**
+ * Relations exposed to IAccessible2.
+ */
+static const uint32_t sRelationTypesForIA2[] = {
+  nsIAccessibleRelation::RELATION_LABELLED_BY,
+  nsIAccessibleRelation::RELATION_LABEL_FOR,
+  nsIAccessibleRelation::RELATION_DESCRIBED_BY,
+  nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
+  nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
+  nsIAccessibleRelation::RELATION_CONTROLLED_BY,
+  nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
+  nsIAccessibleRelation::RELATION_FLOWS_TO,
+  nsIAccessibleRelation::RELATION_FLOWS_FROM,
+  nsIAccessibleRelation::RELATION_MEMBER_OF,
+  nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
+  nsIAccessibleRelation::RELATION_EMBEDS,
+  nsIAccessibleRelation::RELATION_EMBEDDED_BY,
+  nsIAccessibleRelation::RELATION_POPUP_FOR,
+  nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF
+};
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -432,16 +432,29 @@ XULTreeAccessible::ChildCount() const
 
   int32_t rowCount = 0;
   mTreeView->GetRowCount(&rowCount);
   childCount += rowCount;
 
   return childCount;
 }
 
+Relation
+XULTreeAccessible::RelationByType(uint32_t aType)
+{
+  if (aType == nsIAccessibleRelation::RELATION_NODE_PARENT_OF) {
+    if (mTreeView)
+      return Relation(new XULTreeItemIterator(this, mTreeView, -1));
+
+    return Relation();
+  }
+
+  return Accessible::RelationByType(aType);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible: Widgets
 
 bool
 XULTreeAccessible::IsWidget() const
 {
   return true;
 }
@@ -793,28 +806,44 @@ XULTreeItemAccessibleBase::TakeFocus()
 }
 
 Relation
 XULTreeItemAccessibleBase::RelationByType(uint32_t aType)
 {
   if (!mTreeView)
     return Relation();
 
-  if (aType != nsIAccessibleRelation::RELATION_NODE_CHILD_OF)
-    return Relation();
+  switch (aType) {
+    case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
+      int32_t parentIndex = -1;
+      if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
+        return Relation();
+
+      if (parentIndex == -1)
+        return Relation(mParent);
+
+      XULTreeAccessible* treeAcc = mParent->AsXULTree();
+      return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
+    }
 
-  int32_t parentIndex = -1;
-  if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
-    return Relation();
+    case nsIAccessibleRelation::RELATION_NODE_PARENT_OF: {
+      bool isTrue = false;
+      if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue)
+        return Relation();
+
+      if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue)
+        return Relation();
 
-  if (parentIndex == -1)
-    return Relation(mParent);
+      XULTreeAccessible* tree = mParent->AsXULTree();
+      return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
+    }
 
-  XULTreeAccessible* treeAcc = mParent->AsXULTree();
-  return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
+    default:
+      return Relation();
+  }
 }
 
 uint8_t
 XULTreeItemAccessibleBase::ActionCount()
 {
   // "activate" action is available for all treeitems, "expand/collapse" action
   // is avaible for treeitem which is container.
   return IsExpandable() ? 2 : 1;
--- a/accessible/src/xul/XULTreeAccessible.h
+++ b/accessible/src/xul/XULTreeAccessible.h
@@ -42,16 +42,17 @@ public:
   virtual void Value(nsString& aValue);
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild);
 
   virtual Accessible* GetChildAt(uint32_t aIndex);
   virtual uint32_t ChildCount() const;
+  virtual Relation RelationByType(uint32_t aType);
 
   // SelectAccessible
   virtual already_AddRefed<nsIArray> SelectedItems();
   virtual uint32_t SelectedItemCount();
   virtual Accessible* GetSelectedItem(uint32_t aIndex);
   virtual bool IsItemSelected(uint32_t aIndex);
   virtual bool AddItemToSelection(uint32_t aIndex);
   virtual bool RemoveItemFromSelection(uint32_t aIndex);
--- a/accessible/tests/mochitest/actions/test_general.xul
+++ b/accessible/tests/mochitest/actions/test_general.xul
@@ -16,16 +16,22 @@
           src="../common.js" />
   <script type="application/javascript"
           src="../events.js" />
   <script type="application/javascript"
           src="../actions.js" />
 
   <script type="application/javascript">
   <![CDATA[
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    } else {
+      SimpleTest.expectAssertions(1);
+    }
+
     function doTest()
     {
       var actionsArray = [
         {
           ID: "menu",
           actionName: "click",
           events: CLICK_EVENTS,
           // Wait for focus event, it guarantees us the submenu tree is created,
--- a/accessible/tests/mochitest/actions/test_link.html
+++ b/accessible/tests/mochitest/actions/test_link.html
@@ -12,16 +12,24 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../actions.js"></script>
 
   <script type="application/javascript">
+    if (navigator.platform.indexOf("Win") == 0) {
+      SimpleTest.expectAssertions(13);
+    } else if (navigator.platform.indexOf("Linux") == 0) {
+      SimpleTest.expectAssertions(18);
+    } else if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 14);
+    }
+
     function getAnchorTargetDocumentAcc()
     {
       var thisTabDocAcc = getTabDocAccessible();
       var thisDocTabPanelAcc = thisTabDocAcc.parent.parent;
       var tabPanelsAcc = thisDocTabPanelAcc.parent;
       var newDocTabPanelAcc = tabPanelsAcc.lastChild.firstChild;
       return newDocTabPanelAcc.firstChild;
     }
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -14,16 +14,20 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
+    if (navigator.platform.indexOf("Linux") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    }
+
     function doTest()
     {
       // aria
       testAttrs("atomic", {"atomic" : "true"}, true);
       testAttrs("autocomplete", {"autocomplete" : "true"}, true);
       testAttrs("checkbox", {"checkable" : "true"}, true); 
       testAttrs("checkedCheckbox", {"checkable" : "true"}, true); 
       testAttrs("checkedMenuitem", {"checkable" : "true"}, true); 
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -605,16 +605,27 @@ function getTextFromClipboard()
   return "";
 }
 
 /**
  * Return pretty name for identifier, it may be ID, DOM node or accessible.
  */
 function prettyName(aIdentifier)
 {
+  if (aIdentifier instanceof Array) {
+    var msg = "";
+    for (var idx = 0; idx < aIdentifier.length; idx++) {
+      if (msg != "")
+        msg += ", ";
+
+      msg += prettyName(aIdentifier[idx]);
+    }
+    return msg;
+  }
+
   if (aIdentifier instanceof nsIAccessible) {
     var acc = getAccessible(aIdentifier);
     var msg = "[" + getNodePrettyName(acc.DOMNode);
     try {
       msg += ", role: " + roleToString(acc.role);
       if (acc.name)
         msg += ", name: '" + shortenString(acc.name) + "'";
     } catch (e) {
--- a/accessible/tests/mochitest/editabletext/test_1.html
+++ b/accessible/tests/mochitest/editabletext/test_1.html
@@ -14,16 +14,22 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="editabletext.js"></script>
 
   <script type="application/javascript">
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 3);
+    } else {
+      SimpleTest.expectAssertions(3);
+    }
+
     function addTestEditable(aID, aTestRun, aTrailChar)
     {
       var et = new editableTextTest(aID);
 
       //////////////////////////////////////////////////////////////////////////
       // setTextContents
       et.scheduleTest(et.setTextContents, "hello");
       et.scheduleTest(et.setTextContents, "olleh", aTrailChar); // due to some reason this reports '\n' in the end
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -27,16 +27,19 @@
   <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript"
           src="../autocomplete.js" />
 
   <script type="application/javascript">
   <![CDATA[
+    if (navigator.platform.indexOf("Mac") != 0) {
+      SimpleTest.expectAssertions(2);
+    }
     ////////////////////////////////////////////////////////////////////////////
     // Hacky stuffs
 
     // This is the hack needed for searchbar work outside of browser.
     function getBrowser()
     {
       return {
         mCurrentBrowser: { engines: new Array() }
--- a/accessible/tests/mochitest/events/test_focus_browserui.xul
+++ b/accessible/tests/mochitest/events/test_focus_browserui.xul
@@ -19,16 +19,19 @@
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../browser.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
+    if (navigator.platform.indexOf("Linux") == 0) {
+      SimpleTest.expectAssertions(1);
+    }
     ////////////////////////////////////////////////////////////////////////////
     // Helpers
 
     function inputInDocument()
     {
       var tabdoc = currentTabDocument();
       return tabdoc.getElementById("input");
     }
--- a/accessible/tests/mochitest/events/test_focus_doc.html
+++ b/accessible/tests/mochitest/events/test_focus_doc.html
@@ -16,16 +16,24 @@
   <script type="application/javascript"
     src="../events.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
     <script type="application/javascript"
       src="../states.js"></script>
 
   <script type="application/javascript">
+    if (navigator.platform.indexOf("Win") == 0) {
+      SimpleTest.expectAssertions(1, 2);
+    } else if (navigator.platform.indexOf("Linux") == 0) {
+      SimpleTest.expectAssertions(1);
+    } else if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    }
+
     var gQueue = null;
 
     //var gA11yEventDumpID = "eventdump";
     //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       // setup
--- a/accessible/tests/mochitest/relations.js
+++ b/accessible/tests/mochitest/relations.js
@@ -9,16 +9,17 @@ const RELATION_DESCRIPTION_FOR = nsIAcce
 const RELATION_EMBEDDED_BY = nsIAccessibleRelation.RELATION_EMBEDDED_BY;
 const RELATION_EMBEDS = nsIAccessibleRelation.RELATION_EMBEDS;
 const RELATION_FLOWS_FROM = nsIAccessibleRelation.RELATION_FLOWS_FROM;
 const RELATION_FLOWS_TO = nsIAccessibleRelation.RELATION_FLOWS_TO;
 const RELATION_LABEL_FOR = nsIAccessibleRelation.RELATION_LABEL_FOR;
 const RELATION_LABELLED_BY = nsIAccessibleRelation.RELATION_LABELLED_BY;
 const RELATION_MEMBER_OF = nsIAccessibleRelation.RELATION_MEMBER_OF;
 const RELATION_NODE_CHILD_OF = nsIAccessibleRelation.RELATION_NODE_CHILD_OF;
+const RELATION_NODE_PARENT_OF = nsIAccessibleRelation.RELATION_NODE_PARENT_OF;
 const RELATION_PARENT_WINDOW_OF = nsIAccessibleRelation.RELATION_PARENT_WINDOW_OF;
 const RELATION_POPUP_FOR = nsIAccessibleRelation.RELATION_POPUP_FOR;
 const RELATION_SUBWINDOW_OF = nsIAccessibleRelation.RELATION_SUBWINDOW_OF;
 
 ////////////////////////////////////////////////////////////////////////////////
 // General
 
 /**
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -66,31 +66,45 @@
       // aria_owns, multiple relations
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
-      testRelation("treeitem6", RELATION_NODE_CHILD_OF, "treeitem5");
+      testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
+      testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
 
       // 'node child of' relation for row role of treegrid
       testRelation("treegridrow1", RELATION_NODE_CHILD_OF, "treegrid");
       testRelation("treegridrow2", RELATION_NODE_CHILD_OF, "treegrid");
       testRelation("treegridrow3", RELATION_NODE_CHILD_OF, "treegridrow2");
 
       // 'node child of' relation for the document having window, returns
       // direct accessible parent (fixed in bug 419770).
       var iframeElmObj = {};
       var iframeAcc = getAccessible("iframe", null, iframeElmObj);
       var iframeDoc = iframeElmObj.value.contentDocument;
       var iframeDocAcc = getAccessible(iframeDoc);
       testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
 
+      // 'node parent of' relation on ARIA tree and treegrid.
+      testRelation("tree", RELATION_NODE_PARENT_OF,
+                    ["treeitem1", "treeitem2", // aria-owns
+                     "treeitem3", "treeitem4", "treeitem6"]); // children
+      testRelation("treeitem4", RELATION_NODE_PARENT_OF,
+                   "treeitem5"); // aria-level
+      testRelation("treeitem6", RELATION_NODE_PARENT_OF,
+                   "treeitem7"); // // group role
+
+      testRelation("treegridrow2", RELATION_NODE_PARENT_OF, "treegridrow3");
+      testRelation("treegrid", RELATION_NODE_PARENT_OF,
+                   ["treegridrow1", "treegridrow2"]);
+
       // aria-controls
       getAccessible("tab");
       todo(false,
            "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
       testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
       testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
 
       // aria-controls, multiple relations
@@ -136,32 +150,37 @@
 
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
      title="mochitests for accessible relations">
-    Mozilla Bug 475298
+    Bug 475298
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
+     title="Implement RELATION_NODE_PARENT_OF">
+    Bug 527461
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
      title="make HTML <output> accessible">
-    Mozilla Bug 558036
+    Bug 558036
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=682790"
      title="Ignore implicit label association when it's associated explicitly">
-    Mozilla Bug 682790
+    Bug 682790
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=687393"
      title="HTML select options gets relation from containing label">
-    Mozilla Bug 687393
+    Bug 687393
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <label id="label1_1" for="control1_1">label</label>
   <input id="control1_1">
@@ -222,18 +241,19 @@
   <span role="checkbox" id="checkbox5" aria-describedby="descr2 descr3"></span>
 
   <div role="treeitem" id="treeitem1">Yellow</div>
   <div role="treeitem" id="treeitem2">Orange</div>
   <div id="tree" role="tree" aria-owns="treeitem1 treeitem2">
     <div role="treeitem" id="treeitem3">Blue</div>
     <div role="treeitem" id="treeitem4" aria-level="1">Green</div>
     <div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
+    <div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
     <div role="group">
-      <div role="treeitem" id="treeitem6">Super light green</div>
+      <div role="treeitem" id="treeitem7">Super light green</div>
     </div>
   </div>
 
   <div role="treegrid" id="treegrid">
     <div role="row" id="treegridrow1">
       <span role="gridcell">cell1</span><span role="gridcell">cell2</span>
     </div>
     <div role="row" id="treegridrow2" aria-level="1">
--- a/accessible/tests/mochitest/relations/test_tree.xul
+++ b/accessible/tests/mochitest/relations/test_tree.xul
@@ -42,16 +42,21 @@
       testRelation(treeitem4, RELATION_NODE_CHILD_OF, [treeitem2]);
 
       var treeitem5 = treeitem4.nextSibling;
       testRelation(treeitem5, RELATION_NODE_CHILD_OF, [tree]);
 
       var treeitem6 = treeitem5.nextSibling;
       testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
 
+      testRelation(tree, RELATION_NODE_PARENT_OF,
+                   [treeitem1, treeitem2, treeitem5, treeitem6]);
+      testRelation(treeitem2, RELATION_NODE_PARENT_OF,
+                   [treeitem3, treeitem4]);
+
       // treeitems and treecells shouldn't pick up relations from tree
       testRelation(treeitem1, RELATION_LABELLED_BY, null);
       testRelation(treeitem1.firstChild, RELATION_LABELLED_BY, null);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
@@ -59,22 +64,27 @@
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
          title="Reorganize implementation of XUL tree accessibility">
-        Mozilla Bug 503727
+        Bug 503727
+      </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
+         title="Implement RELATION_NODE_PARENT_OF">
+        Bug 527461
       </a>
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=691248"
          title="XUL tree items shouldn't pick up relations from XUL tree">
-        Mozilla Bug 691248
+        Bug 691248
       </a>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
--- a/accessible/tests/mochitest/relations/test_ui_modalprompt.html
+++ b/accessible/tests/mochitest/relations/test_ui_modalprompt.html
@@ -17,16 +17,22 @@
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../browser.js"></script>
 
   <script type="application/javascript">
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    } else {
+      SimpleTest.expectAssertions(1);
+    }
+
     function hasTabModalPrompts() {
       try {
         return SpecialPowers.getBoolPref("prompts.tab_modal.enabled");
       } catch (ex) {
         return false;
       }
     }
 
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -6,17 +6,22 @@
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
-    
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 23);
+    } else {
+      SimpleTest.expectAssertions(23);
+    }
+
     function doTest()
     {
       // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 
       ////////////////////////////////////////////////////////////////////////
       // characterCount
 
--- a/accessible/tests/mochitest/text/test_whitespaces.html
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -8,17 +8,22 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
 
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
-    
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 10);
+    } else {
+      SimpleTest.expectAssertions(10);
+    }
+
     function doTest()
     {
       // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
 
       ////////////////////////////////////////////////////////////////////////
       // characterCount
 
--- a/accessible/tests/mochitest/text/test_words.html
+++ b/accessible/tests/mochitest/text/test_words.html
@@ -7,17 +7,22 @@
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
-    
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    } else {
+      SimpleTest.expectAssertions(1);
+    }
+
     function doTest()
     {
       // "one two"
       testWords("div1", ["one", "two"]);
 
       // "one  two"
       testWords("div2", ["one", "two"]);
 
--- a/accessible/tests/mochitest/tree/test_dockids.html
+++ b/accessible/tests/mochitest/tree/test_dockids.html
@@ -11,16 +11,20 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
 
   <script type="application/javascript">
+  if (navigator.platform.indexOf("Win") == 0) {
+    SimpleTest.expectAssertions(0, 1);
+  }
+
   function doTest()
   {
     var tree =
      { DOCUMENT: [
        { PARAGRAPH: [ // head
          { PARAGRAPH: [ // link
            { STATICTEXT: [] }, // generated content
            { STATICTEXT: [] } // generated content
--- a/accessible/tests/mochitest/tree/test_txtctrl.xul
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xul
@@ -13,16 +13,21 @@
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
+    if (navigator.platform.indexOf("Mac") == 0) {
+      SimpleTest.expectAssertions(0, 1);
+    } else {
+      SimpleTest.expectAssertions(1);
+    }
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTest()
     {
       ////////////////////
       // textbox
       ////////////////////
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -630,16 +630,18 @@ pref("font.size.inflation.disabledInMast
 // Enable freeing dirty pages when minimizing memory; this reduces memory
 // consumption when applications are sent to the background.
 pref("memory.free_dirty_pages", true);
 
 // UAProfile settings
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
 
+pref("layout.imagevisibility.enabled", false);
+
 // Enable native identity (persona/browserid)
 pref("dom.identity.enabled", true);
 
 // Wait up to this much milliseconds when orientation changed
 pref("layers.orientation.sync.timeout", 1000);
 
 // Don't discard WebGL contexts for foreground apps on memory
 // pressure.
@@ -650,8 +652,10 @@ pref("webgl.can-lose-context-in-foregrou
 pref("memory_info_dumper.watch_fifo.enabled", true);
 pref("memory_info_dumper.watch_fifo.directory", "/data/local");
 
 // <input type='file'> implementation is not complete. We have to disable the
 // type to web content to help them do feature detection.
 pref("dom.disable_input_file", true);
 
 pref("general.useragent.enable_overrides", true);
+
+pref("b2g.version", @MOZ_B2G_VERSION@);
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -150,16 +150,21 @@ SettingsListener.observe('language.curre
   ['ril.mms.mmsport'].forEach(function(key) {
     SettingsListener.observe(key, null, function(value) {
       if (value != null) {
         Services.prefs.setIntPref(key, value);
       }
     });
   });
 
+  SettingsListener.observe('ril.mms.retrieval_mode', 'manual',
+    function(value) {
+      Services.prefs.setCharPref('dom.mms.retrieval_mode', value);
+  });
+
   SettingsListener.observe('ril.sms.strict7BitEncoding.enabled', false,
     function(value) {
       Services.prefs.setBoolPref('dom.sms.strict7BitEncoding', value);
   });
 })();
 
 //=================== DeviceInfo ====================
 Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -43,16 +43,17 @@ let log =
 
 function DirectoryProvider() {
 }
 
 DirectoryProvider.prototype = {
   classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
+  _xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
 
   getFile: function dp_getFile(prop, persistent) {
 #ifdef MOZ_WIDGET_GONK
     let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
                       "permissionDBPDir", "UpdRootD"];
     if (localProps.indexOf(prop) != -1) {
       let file = Cc["@mozilla.org/file/local;1"]
                    .createInstance(Ci.nsILocalFile)
@@ -100,16 +101,17 @@ DirectoryProvider.prototype = {
     }
     return requiredSpace <= stat.freeBytes;
   },
 
   findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
     if (!Services.volumeService) {
       return this.createUpdatesDir(LOCAL_DIR, subdir);
     }
+
     let activeUpdate = Services.um.activeUpdate;
     if (gUseSDCard) {
       if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
         let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
         if (extUpdateDir !== null) {
           return extUpdateDir;
         }
         log("Warning: " + gExtStorage + " has enough free space for update " +
@@ -153,17 +155,18 @@ DirectoryProvider.prototype = {
       return updateDir;
     }
 
     // If we've gotten this far, there isn't enough free space to download the patch
     // on either external storage or /data/local. All we can do is report the
     // error and let upstream code handle it more gracefully.
     log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
         " update " + activeUpdate.name);
-    throw Cr.NS_ERROR_FILE_TOO_BIG;
+    activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
+    return null;
   },
 
   createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
       let dir = Cc["@mozilla.org/file/local;1"]
                    .createInstance(Ci.nsILocalFile);
       dir.initWithPath(root);
       if (!dir.isWritable()) {
         log("Error: " + dir.path + " isn't writable");
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -20,17 +20,16 @@ let log =
   function log_noop(msg) { };
 
 const PREF_APPLY_PROMPT_TIMEOUT          = "b2g.update.apply-prompt-timeout";
 const PREF_APPLY_IDLE_TIMEOUT            = "b2g.update.apply-idle-timeout";
 const PREF_DOWNLOAD_WATCHDOG_TIMEOUT     = "b2g.update.download-watchdog-timeout";
 const PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES = "b2g.update.download-watchdog-max-retries";
 
 const NETWORK_ERROR_OFFLINE = 111;
-const FILE_ERROR_TOO_BIG    = 112;
 const HTTP_ERROR_OFFSET     = 1000;
 
 const STATE_DOWNLOADING = 'downloading';
 
 XPCOMUtils.defineLazyServiceGetter(Services, "aus",
                                    "@mozilla.org/updates/update-service;1",
                                    "nsIApplicationUpdateService");
 
@@ -309,17 +308,18 @@ UpdatePrompt.prototype = {
     // Services.aus.downloadUpdate will return immediately and not
     // call showUpdateDownloaded, so we detect this.
     if (aUpdate.state == "applied" && aUpdate.errorCode == 0) {
       this.showUpdateDownloaded(aUpdate, true);
       return;
     }
 
     log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode);
-    if (aUpdate.errorCode == FILE_ERROR_TOO_BIG) {
+    let errorCode = aUpdate.errorCode >>> 0;
+    if (errorCode == Cr.NS_ERROR_FILE_TOO_BIG) {
       aUpdate.statusText = "file-too-big";
     }
     this.showUpdateError(aUpdate);
   },
 
   handleDownloadCancel: function UP_handleDownloadCancel() {
     log("Pausing download");
     Services.aus.pauseDownload();
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -358,18 +358,16 @@
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
 @BINPATH@/components/BrowserPlaces.manifest
-@BINPATH@/components/nsPrivateBrowsingService.manifest
-@BINPATH@/components/nsPrivateBrowsingService.js
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsTryToClose.manifest
 @BINPATH@/components/nsTryToClose.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
 @BINPATH@/components/nsLoginManagerPrompter.js
 @BINPATH@/components/SiteSpecificUserAgent.js
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -83,22 +83,20 @@ var FullScreen = {
   },
 
   exitDomFullScreen : function() {
     document.mozCancelFullScreen();
   },
 
   handleEvent: function (event) {
     switch (event.type) {
-      case "deactivate":
-        // We must call exitDomFullScreen asynchronously, since "deactivate" is
-        // dispatched in the middle of the focus manager's window lowering code,
-        // and the focus manager gets confused if we exit fullscreen mode in the
-        // middle of window lowering. See bug 729872.
-        setTimeout(this.exitDomFullScreen.bind(this), 0);
+      case "activate":
+        if (document.mozFullScreen) {
+          this.showWarning(this.fullscreenDoc);
+        }
         break;
       case "transitionend":
         if (event.propertyName == "opacity")
           this.cancelWarning();
         break;
     }
   },
 
@@ -133,20 +131,21 @@ var FullScreen = {
 
     this.showWarning(event.target);
 
     // Exit DOM full-screen mode upon open, close, or change tab.
     gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
     gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
     gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
 
-    // Exit DOM full-screen mode when the browser window loses focus (ALT+TAB, etc).
-    if (!this.useLionFullScreen &&
-        gPrefService.getBoolPref("full-screen-api.exit-on-deactivate")) {
-      window.addEventListener("deactivate", this);
+    // Add listener to detect when the fullscreen window is re-focused.
+    // If a fullscreen window loses focus, we show a warning when the
+    // fullscreen window is refocused.
+    if (!this.useLionFullScreen) {
+      window.addEventListener("activate", this);
     }
 
     // Cancel any "hide the toolbar" animation which is in progress, and make
     // the toolbar hide immediately.
     this._cancelAnimation();
     this.mouseoverToggle(false);
 
     // Remove listeners on the full-screen toggler, so that mouseover
@@ -166,17 +165,18 @@ var FullScreen = {
 
       this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
       this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
       this.cancelWarning();
       gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
       if (!this.useLionFullScreen)
-        window.removeEventListener("deactivate", this);
+        window.removeEventListener("activate", this);
+      this.fullscreenDoc = null;
     }
   },
 
   observe: function(aSubject, aTopic, aData)
   {
     if (aData == "browser.fullscreen.autohide") {
       if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
         gBrowser.mPanelContainer.addEventListener("mousemove",
@@ -302,17 +302,16 @@ var FullScreen = {
     clearTimeout(this._animationTimeout);
     this._isAnimating = false;
     this._shouldAnimate = false;
   },
 
   cancelWarning: function(event) {
     if (!this.warningBox)
       return;
-    this.fullscreenDoc = null;
     this.warningBox.removeEventListener("transitionend", this);
     if (this.warningFadeOutTimeout) {
       clearTimeout(this.warningFadeOutTimeout);
       this.warningFadeOutTimeout = null;
     }
 
     // Ensure focus switches away from the (now hidden) warning box. If the user
     // clicked buttons in the fullscreen key authorization UI, it would have been
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -26,45 +26,46 @@ let SocialUI = {
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
 
     // Called when we enter DOM full-screen mode.
     window.addEventListener("mozfullscreenchange", function () {
       SocialSidebar.update();
       SocialChatBar.update();
     });
 
-    Social.init(this._providerReady.bind(this));
+    SocialChatBar.init();
+    SocialShareButton.init();
+    SocialMenu.init();
+    SocialToolbar.init();
+    SocialSidebar.init();
+
+    Social.init();
+    // If social was previously initialized it isn't going to notify observers
+    // about the provider being set or the list of providers changing, so
+    // handle those now.
+    if (Social.provider) {
+      this.observe(null, "social:provider-set", Social.provider.origin);
+      this.observe(null, "social:providers-changed", null);
+    }
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:pref-changed");
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
     Services.obs.removeObserver(this, "social:recommend-info-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:provider-set");
     Services.obs.removeObserver(this, "social:providers-changed");
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
-  // Called once, after window load, once Social.jsm's provider has been set.
-  _providerReady: function SocialUI_providerReady() {
-    this._updateActiveUI();
-    this._updateMenuItems();
-
-    SocialChatBar.update();
-    SocialShareButton.init();
-    SocialMenu.populate();
-    SocialToolbar.init();
-    SocialSidebar.init();
-  },
-
   // Social.provider has changed, update any state that depends on it.
   // Note: this method is not called when Social.provider is first set, during
   // the first window load.
   _updateProvider: function () {
     // XXX audit for handling nullness of social.provider
     this._updateActiveUI();
     this._updateMenuItems();
 
@@ -330,16 +331,18 @@ let SocialUI = {
     if (this._chromeless || PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
     return !!Social.provider;
   },
 
 }
 
 let SocialChatBar = {
+  init: function() {
+  },
   get chatbar() {
     return document.getElementById("pinnedchats");
   },
   // Whether the chatbar is available for this window.  Note that in full-screen
   // mode chats are available, but not shown.
   get isAvailable() {
     return SocialUI.enabled && Social.haveLoggedInUser();
   },
@@ -551,17 +554,16 @@ let SocialFlyout = {
     }
     this.yOffset = yOffset;
   }
 }
 
 let SocialShareButton = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SSB_init() {
-    this.updateProvider();
   },
 
   // Called when the Social.provider changes
   updateProvider: function () {
     this.updateButtonHiddenState();
     if (!Social.provider)
       return;
     this.updateProfileInfo();
@@ -698,16 +700,19 @@ let SocialShareButton = {
       shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
       imageURL = recommendInfo.images.share;
     }
     shareButton.src = imageURL;
   }
 };
 
 var SocialMenu = {
+  init: function SocialMenu_init() {
+  },
+
   populate: function SocialMenu_populate() {
     let submenu = document.getElementById("menu_social-statusarea-popup");
     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
     while (ambientMenuItems.length)
       submenu.removeChild(ambientMenuItems.item(0));
 
     let separator = document.getElementById("socialAmbientMenuSeparator");
     separator.hidden = true;
@@ -735,18 +740,16 @@ var SocialMenu = {
 // XXX Need to audit that this is being initialized correctly
 var SocialToolbar = {
   // Called once, after window load, when the Social.provider object is
   // initialized.
   init: function SocialToolbar_init() {
     let accesskey = gNavigatorBundle.getString("social.removeProvider.accesskey");
     let removeCommand = document.getElementById("Social:Remove");
     removeCommand.setAttribute("accesskey", accesskey);
-
-    this.updateProvider();
     this._dynamicResizer = new DynamicResizeWatcher();
   },
 
   // Called when the Social.provider changes
   updateProvider: function () {
     let provider = Social.provider || Social.defaultProvider;
     if (provider) {
       let label = gNavigatorBundle.getFormattedString("social.removeProvider.label",
@@ -1068,17 +1071,16 @@ var SocialToolbar = {
 
 var SocialSidebar = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SocialSidebar_init() {
     let sbrowser = document.getElementById("social-sidebar-browser");
     Social.setErrorListener(sbrowser, this.setSidebarErrorMessage.bind(this));
     // setting isAppTab causes clicks on untargeted links to open new tabs
     sbrowser.docShell.isAppTab = true;
-    this.update();
   },
 
   // Whether the sidebar can be shown for this window.
   get canShow() {
     return SocialUI.enabled && Social.provider.sidebarURL;
   },
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -392,16 +392,24 @@ window[chromehidden~="toolbar"] toolbar:
 }
 
 #full-screen-warning-container[fade-warning-out] {
   transition-property: opacity !important;
   transition-duration: 500ms !important;
   opacity: 0.0;
 }
 
+/* When the modal fullscreen approval UI is showing, don't allow interaction
+   with the page, but when we're just showing the warning upon entering
+   fullscreen on an already approved page, do allow interaction with the page.
+ */
+#full-screen-warning-container:not([obscure-browser]) {
+  pointer-events: none;
+}
+
 #full-screen-warning-message {
   /* We must specify a max-width, otherwise word-wrap:break-word doesn't
      work in descendant <description> and <label> elements. Bug 630864. */
   max-width: 800px;
 }
 
 #full-screen-domain-text,
 #full-screen-remember-decision > .checkbox-label-box > .checkbox-label {
--- a/browser/base/content/test/test_bug787619.html
+++ b/browser/base/content/test/test_bug787619.html
@@ -13,17 +13,17 @@
 
   <a id="wrapper">
     <embed id="plugin" style="width: 200px; height: 200px" type="application/x-test">
   </a>
 
   <script class="testbody" type="application/javascript;version=1.7">
   SimpleTest.waitForExplicitFinish();
 
-  const Ci = Components.interfaces;
+  const Ci = SpecialPowers.Ci;
   let wrapperClickCount = 0;
 
   function test1() {
     let plugin = document.getElementById('plugin');
     ok(plugin, 'got plugin element');
     let objLC = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     ok(!objLC.activated, 'plugin should not be activated');
 
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -948,17 +948,17 @@ function startTest() {
     ok(contextMenu, "Got context menu XUL");
 
     if (chromeWin.document.getElementById("Browser:Stop").getAttribute("disabled") != "true") {
       todo(false, "Wait for subwindow to load... (This should usually happen once.)");
       SimpleTest.executeSoon(startTest);
       return;
     }
 
-    subwindow.allowfullscreen = true;
+    subwindow.allowFullscreen = true;
     lastElement = null;
 
     text   = subwindow.document.getElementById("test-text");
     link   = subwindow.document.getElementById("test-link");
     imagelink = subwindow.document.getElementById("test-image-link");
     mailto = subwindow.document.getElementById("test-mailto");
     input  = subwindow.document.getElementById("test-input");
     img    = subwindow.document.getElementById("test-image");
--- a/browser/branding/aurora/Makefile.in
+++ b/browser/branding/aurora/Makefile.in
@@ -64,9 +64,16 @@ BRANDING_FILES := \
 	document-os2.ico \
 	$(NULL)
 endif
 
 BRANDING_DEST := $(DIST)/branding
 BRANDING_TARGET := export
 INSTALL_TARGETS += BRANDING
 
+ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro)
+VISUALMANIFEST := VisualElementsManifest.xml
+VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME}
+VISUALMANIFEST_PATH := $(DIST)/bin
+PP_TARGETS += VISUALMANIFEST
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/branding/aurora/VisualElementsManifest.xml
@@ -0,0 +1,16 @@
+<Application
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <VisualElements
+      DisplayName="@MOZ_APP_DISPLAYNAME@"
+      Logo="tileresources\VisualElements_logo.png"
+      SmallLogo="tileresources\VisualElements_smalllogo.png"
+      ForegroundText="light"
+      BackgroundColor="#1c112e">
+    <DefaultTile
+        ShortName="@MOZ_APP_DISPLAYNAME@"
+        ShowName="allLogos"
+            />
+    <SplashScreen
+        Image="tileresources\VisualElements_splashscreen.png" />
+  </VisualElements>
+</Application>
--- a/browser/branding/nightly/Makefile.in
+++ b/browser/branding/nightly/Makefile.in
@@ -64,9 +64,16 @@ BRANDING_FILES := \
 	document-os2.ico \
 	$(NULL)
 endif
 
 BRANDING_DEST := $(DIST)/branding
 BRANDING_TARGET := export
 INSTALL_TARGETS += BRANDING
 
+ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro)
+VISUALMANIFEST := VisualElementsManifest.xml
+VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME}
+VISUALMANIFEST_PATH := $(DIST)/bin
+PP_TARGETS += VISUALMANIFEST
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/branding/nightly/VisualElementsManifest.xml
@@ -0,0 +1,16 @@
+<Application
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <VisualElements
+      DisplayName="@MOZ_APP_DISPLAYNAME@"
+      Logo="tileresources\VisualElements_logo.png"
+      SmallLogo="tileresources\VisualElements_smalllogo.png"
+      ForegroundText="light"
+      BackgroundColor="#001226">
+    <DefaultTile
+        ShortName="@MOZ_APP_DISPLAYNAME@"
+        ShowName="allLogos"
+            />
+    <SplashScreen
+        Image="tileresources\VisualElements_splashscreen.png" />
+  </VisualElements>
+</Application>
--- a/browser/branding/official/Makefile.in
+++ b/browser/branding/official/Makefile.in
@@ -64,9 +64,16 @@ BRANDING_FILES := \
 	document-os2.ico \
 	$(NULL)
 endif
 
 BRANDING_DEST := $(DIST)/branding
 BRANDING_TARGET := export
 INSTALL_TARGETS += BRANDING
 
+ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro)
+VISUALMANIFEST := VisualElementsManifest.xml
+VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME}
+VISUALMANIFEST_PATH := $(DIST)/bin
+PP_TARGETS += VISUALMANIFEST
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/branding/official/VisualElementsManifest.xml
@@ -0,0 +1,16 @@
+<Application
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <VisualElements
+      DisplayName="@MOZ_APP_DISPLAYNAME@"
+      Logo="tileresources\VisualElements_logo.png"
+      SmallLogo="tileresources\VisualElements_smalllogo.png"
+      ForegroundText="light"
+      BackgroundColor="#1895f5">
+    <DefaultTile
+        ShortName="@MOZ_APP_DISPLAYNAME@"
+        ShowName="allLogos"
+            />
+    <SplashScreen
+        Image="tileresources\VisualElements_splashscreen.png" />
+  </VisualElements>
+</Application>
--- a/browser/branding/unofficial/Makefile.in
+++ b/browser/branding/unofficial/Makefile.in
@@ -64,9 +64,16 @@ BRANDING_FILES := \
 	document-os2.ico \
 	$(NULL)
 endif
 
 BRANDING_DEST := $(DIST)/branding
 BRANDING_TARGET := export
 INSTALL_TARGETS += BRANDING
 
+ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro)
+VISUALMANIFEST := VisualElementsManifest.xml
+VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME}
+VISUALMANIFEST_PATH := $(DIST)/bin
+PP_TARGETS += VISUALMANIFEST
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/branding/unofficial/VisualElementsManifest.xml
@@ -0,0 +1,16 @@
+<Application
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <VisualElements
+      DisplayName="@MOZ_APP_DISPLAYNAME@"
+      Logo="tileresources\VisualElements_logo.png"
+      SmallLogo="tileresources\VisualElements_smalllogo.png"
+      ForegroundText="light"
+      BackgroundColor="#001226">
+    <DefaultTile
+        ShortName="@MOZ_APP_DISPLAYNAME@"
+        ShowName="allLogos"
+            />
+    <SplashScreen
+        Image="tileresources\VisualElements_splashscreen.png" />
+  </VisualElements>
+</Application>
--- a/browser/components/privatebrowsing/Makefile.in
+++ b/browser/components/privatebrowsing/Makefile.in
@@ -6,13 +6,11 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = privatebrowsing
 
-DIRS = src
-
 TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/components/privatebrowsing/src/Makefile.in
+++ /dev/null
@@ -1,23 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH   = @DEPTH@
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH   = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE = privatebrowsing
-LIBRARY_NAME = privatebrowsing_s
-
-FORCE_STATIC_LIB = 1
-USE_STATIC_LIBS = 1
-
-EXTRA_COMPONENTS = \
-  nsPrivateBrowsingServiceObsolete.manifest \
-  nsPrivateBrowsingServiceObsolete.js \
-  $(NULL)
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceObsolete.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const Ci = Components.interfaces;
-
-// This dummy object just emulates the old nsIPrivateBrowsingService, and is
-// non-functional in every aspect.  It is only used to make Jetpack work
-// again.
-
-function PrivateBrowsingService() {
-}
-
-PrivateBrowsingService.prototype = {
-  classID: Components.ID("{ba0e4d3d-7be2-41a0-b723-a7c16b22ebe9}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivateBrowsingService])
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PrivateBrowsingService]);
deleted file mode 100644
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceObsolete.manifest
+++ /dev/null
@@ -1,15 +0,0 @@
-# WebappRT doesn't need these instructions, and they don't necessarily work
-# with it, but it does use a GRE directory that the GRE shares with Firefox,
-# so in order to prevent the instructions from being processed for WebappRT,
-# we need to restrict them to the applications that depend on them, i.e.:
-#
-#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
-#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
-#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
-#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
-#
-# In theory we should do this for all these instructions, but in practice it is
-# sufficient to do it for the app-startup one, and the file is simpler that way.
-
-component {ba0e4d3d-7be2-41a0-b723-a7c16b22ebe9} nsPrivateBrowsingServiceObsolete.js
-contract @mozilla.org/privatebrowsing;1 {ba0e4d3d-7be2-41a0-b723-a7c16b22ebe9}
--- a/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
+++ b/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
@@ -3,16 +3,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that chrome debugging works.
 
 var gClient = null;
 var gTab = null;
+var gHomeTab = null;
 var gThreadClient = null;
 var gNewGlobal = false;
 var gAttached = false;
 var gChromeScript = false;
 const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
 
 function test()
 {
@@ -28,18 +29,17 @@ function test()
         gClient.addListener("newScript", onNewScript);
 
         gClient.attachThread(dbg, function(aResponse, aThreadClient) {
           gThreadClient = aThreadClient;
           ok(!aResponse.error, "Attached to the chrome debugger.");
           gAttached = true;
 
           // Ensure that a new global will be created.
-          let frame = content.document.createElement("iframe");
-          content.document.querySelector("body").appendChild(frame);
+          gHomeTab = gBrowser.addTab("about:home");
 
           finish_test();
         });
       });
     });
   });
 }
 
@@ -53,16 +53,17 @@ function onNewScript(aEvent, aScript)
 
 function finish_test()
 {
   if (!gAttached || !gChromeScript) {
     return;
   }
   gClient.removeListener("newScript", onNewScript);
   gThreadClient.resume(function(aResponse) {
+    removeTab(gHomeTab);
     removeTab(gTab);
     gClient.close(function() {
       ok(gNewGlobal, "Received newGlobal event.");
       ok(gChromeScript, "Received newScript event for a chrome: script.");
       finish();
     });
   });
 }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -344,18 +344,16 @@
 @BINPATH@/browser/components/nsBrowserGlue.js
 @BINPATH@/browser/components/nsSetDefaultBrowser.manifest
 @BINPATH@/browser/components/nsSetDefaultBrowser.js
 @BINPATH@/browser/components/BrowserDownloads.manifest
 @BINPATH@/browser/components/DownloadsStartup.js
 @BINPATH@/browser/components/DownloadsUI.js
 @BINPATH@/browser/components/BrowserPlaces.manifest
 @BINPATH@/components/BrowserPageThumbs.manifest
-@BINPATH@/browser/components/nsPrivateBrowsingServiceObsolete.manifest
-@BINPATH@/browser/components/nsPrivateBrowsingServiceObsolete.js
 @BINPATH@/components/SiteSpecificUserAgent.js
 @BINPATH@/components/SiteSpecificUserAgent.manifest
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -22,17 +22,16 @@ browser/components/feeds/public/Makefile
 browser/components/feeds/src/Makefile
 browser/components/migration/Makefile
 browser/components/migration/public/Makefile
 browser/components/migration/src/Makefile
 browser/components/places/Makefile
 browser/components/places/src/Makefile
 browser/components/preferences/Makefile
 browser/components/privatebrowsing/Makefile
-browser/components/privatebrowsing/src/Makefile
 browser/components/search/Makefile
 browser/components/sessionstore/Makefile
 browser/components/sessionstore/src/Makefile
 browser/components/sidebar/Makefile
 browser/components/shell/Makefile
 browser/components/shell/public/Makefile
 browser/components/shell/src/Makefile
 browser/components/tabview/Makefile
--- a/browser/metro/base/content/bindings/tabs.xml
+++ b/browser/metro/base/content/bindings/tabs.xml
@@ -101,17 +101,42 @@
       <xul:arrowscrollbox anonid="tabs-scrollbox" class="tabs-scrollbox" flex="1" orient="horizontal"
         clicktoscroll="true" />
     </content>
 
     <handlers>
       <handler event="dblclick" action="this._onDoubleClick();"/>
     </handlers>
 
-    <implementation>
+    <implementation implements="nsIDOMEventListener">
+      <constructor>
+        <![CDATA[
+          window.addEventListener("MozContextUIExpand", this, true);
+        ]]>
+      </constructor>
+
+      <destructor>
+        <![CDATA[
+          window.removeEventListener("MozContextUIExpand", this, true);
+        ]]>
+      </destructor>
+
+      <method name="handleEvent">
+        <parameter name="event"/>
+        <body>
+          <![CDATA[
+            switch (event.type) {
+              case "MozContextUIExpand":
+                this.strip.ensureElementIsVisible(this.selectedTab, false);
+                break;
+            }
+          ]]>
+        </body>
+      </method>
+
       <field name="strip">document.getAnonymousElementByAttribute(this, "anonid", "tabs-scrollbox");</field>
       <field name="_selectedTab">null</field>
 
       <!-- Used by the chrome input handler -->
       <property name="anonScrollBox"
                 readonly="true"
                 onget="return this.strip;"/>
 
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -8,67 +8,55 @@ dump("### ContextMenuHandler.js loaded\n
 
 var ContextMenuHandler = {
   _types: [],
 
   init: function ch_init() {
     // Events we catch from content during the bubbling phase
     addEventListener("contextmenu", this, false);
     addEventListener("pagehide", this, false);
-    addEventListener("select", this, false);
 
     // Messages we receive from browser
+    // Command sent over from browser that only we can handle.
     addMessageListener("Browser:ContextCommand", this, false);
+    // InvokeContextAtPoint is sent to us from browser's selection
+    // overlay when it traps a contextmenu event. In response we
+    // should invoke context menu logic at the point specified.
+    addMessageListener("Browser:InvokeContextAtPoint", this, false);
 
     this.popupNode = null;
   },
 
-  _getLinkURL: function ch_getLinkURL(aLink) {
-    let href = aLink.href;
-    if (href)
-      return href;
-
-    href = aLink.getAttributeNS(kXLinkNamespace, "href");
-    if (!href || !href.match(/\S/)) {
-      // Without this we try to save as the current doc,
-      // for example, HTML case also throws if empty
-      throw "Empty href";
-    }
-
-    return Util.makeURLAbsolute(aLink.baseURI, href);
-  },
-
-  _getURI: function ch_getURI(aURL) {
-    try {
-      return Util.makeURI(aURL);
-    } catch (ex) { }
-
-    return null;
-  },
-
-  _getProtocol: function ch_getProtocol(aURI) {
-    if (aURI)
-      return aURI.scheme;
-    return null;
-  },
-
   handleEvent: function ch_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "contextmenu":
         this._onContentContextMenu(aEvent);
         break;
       case "pagehide":
         this.reset();
         break;
-      case "select":
-        break;
     }
   },
 
   receiveMessage: function ch_receiveMessage(aMessage) {
+    switch (aMessage.name) {
+      case "Browser:ContextCommand":
+        this._onContextCommand(aMessage);
+      break;
+      case "Browser:InvokeContextAtPoint":
+        this._onContextAtPoint(aMessage);
+      break;
+    }
+  },
+
+  /*
+   * Handler for commands send over from browser's ContextCommands.js
+   * in response to certain context menu actions only we can handle.
+   */
+  _onContextCommand: function _onContextCommand(aMessage) {
     let node = this.popupNode;
     let command = aMessage.json.command;
 
     switch (command) {
       case "play":
       case "pause":
         if (node instanceof Ci.nsIDOMHTMLMediaElement)
           node[command]();
@@ -92,48 +80,128 @@ var ContextMenuHandler = {
         break;
 
       case "copy-image-contents":
         this._onCopyImage();
         break;
     }
   },
 
+  /*
+   * Handler for selection overlay context menu events.
+   */
+  _onContextAtPoint: function _onContextCommand(aMessage) {
+    // we need to find popupNode as if the context menu were
+    // invoked on underlying content.
+    let elem = elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
+    this._processPopupNode(elem, aMessage.json.xPos, aMessage.json.yPos,
+                           Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
+  },
+
   /******************************************************
    * Event handlers
    */
 
   reset: function ch_reset() {
     this.popupNode = null;
     this._target = null;
   },
 
-  // content contextmenu
+  // content contextmenu handler
   _onContentContextMenu: function _onContentContextMenu(aEvent) {
     if (aEvent.defaultPrevented)
       return;
 
     // Don't let these bubble up to input.js
     aEvent.stopPropagation();
     aEvent.preventDefault();
 
+    this._processPopupNode(aEvent.originalTarget, aEvent.clientX,
+                           aEvent.clientY, aEvent.mozInputSource);
+  },
+
+  /******************************************************
+   * ContextCommand handlers
+   */
+
+  _onSelectAll: function _onSelectAll() {
+    if (this._isTextInput(this._target)) {
+      // select all text in the input control
+      this._target.select();
+    } else {
+      // select the entire document
+      content.getSelection().selectAllChildren(content.document);
+    }
+    this.reset();
+  },
+
+  _onPaste: function _onPaste() {
+    // paste text if this is an input control
+    if (this._isTextInput(this._target)) {
+      let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
+      if (edit) {
+        edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
+      } else {
+        Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
+      }
+    }
+    this.reset();
+  },
+
+  _onCopyImage: function _onCopyImage() {
+    Util.copyImageToClipboard(this._target);
+  },
+
+  /******************************************************
+   * Utility routines
+   */
+
+   /*
+    * _translateToTopLevelWindow - Given a potential coordinate set within
+    * a subframe, translate up to the parent window which is what front
+    * end code expect.
+    */
+  _translateToTopLevelWindow: function _translateToTopLevelWindow(aPopupNode) {
+    let offsetX = 0;
+    let offsetY = 0;
+    let element = aPopupNode;
+    while (element &&
+           element.ownerDocument &&
+           element.ownerDocument.defaultView != content) {
+      element = element.ownerDocument.defaultView.frameElement;
+      let rect = element.getBoundingClientRect();
+      offsetX += rect.left;
+      offsetY += rect.top;
+    }
+    return { offsetX: offsetX, offsetY: offsetY };
+  },
+
+  /*
+   * _processPopupNode - Generate and send a Content:ContextMenu message
+   * to browser detailing the underlying content types at this.popupNode.
+   * Note the event we receive targets the sub frame (if there is one) of
+   * the page.
+   */
+  _processPopupNode: function _processPopupNode(aPopupNode, aX, aY, aInputSrc) {
+    if (!aPopupNode)
+      return;
+    let { offsetX: offsetX, offsetY: offsetY } =
+      this._translateToTopLevelWindow(aPopupNode);
+    let popupNode = this.popupNode = aPopupNode;
+    let imageUrl = "";
+
     let state = {
       types: [],
       label: "",
       linkURL: "",
       linkTitle: "",
       linkProtocol: null,
       mediaURL: "",
-      x: aEvent.x,
-      y: aEvent.y
     };
 
-    let popupNode = this.popupNode = aEvent.originalTarget;
-    let imageUrl = "";
-
     // Do checks for nodes that never have children.
     if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
       // See if the user clicked on an image.
       if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) {
         state.types.push("image");
         state.label = state.mediaURL = popupNode.currentURI.spec;
         imageUrl = state.mediaURL;
         this._target = popupNode;
@@ -243,60 +311,27 @@ var ContextMenuHandler = {
             state.types.indexOf("link") == -1 &&
             state.types.indexOf("input-text") == -1) {
           state.types.push("content-text");
         }
       }
     }
 
     // populate position and event source
-    state.xPos = aEvent.clientX;
-    state.yPos = aEvent.clientY;
-    state.source = aEvent.mozInputSource;
+    state.xPos = offsetX + aX;
+    state.yPos = offsetY + aY;
+    state.source = aInputSrc;
 
     for (let i = 0; i < this._types.length; i++)
       if (this._types[i].handler(state, popupNode))
         state.types.push(this._types[i].name);
 
     sendAsyncMessage("Content:ContextMenu", state);
   },
 
-  _onSelectAll: function _onSelectAll() {
-    if (this._isTextInput(this._target)) {
-      // select all text in the input control
-      this._target.select();
-    } else {
-      // select the entire document
-      content.getSelection().selectAllChildren(content.document);
-    }
-    this.reset();
-  },
-
-  _onPaste: function _onPaste() {
-    // paste text if this is an input control
-    if (this._isTextInput(this._target)) {
-      let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
-      if (edit) {
-        edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
-      } else {
-        Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
-      }
-    }
-    this.reset();
-  },
-
-  _onCopyImage: function _onCopyImage() {
-    Util.copyImageToClipboard(this._target);
-  },
-
-  /*
-   * Utility routines used in testing for various
-   * HTML element types.
-   */
-
   _isTextInput: function _isTextInput(element) {
     return ((element instanceof Ci.nsIDOMHTMLInputElement &&
              element.mozIsTextField(false)) ||
             element instanceof Ci.nsIDOMHTMLTextAreaElement);
   },
 
   _isLink: function _isLink(element) {
     return ((element instanceof Ci.nsIDOMHTMLAnchorElement && element.href) ||
@@ -310,16 +345,45 @@ var ContextMenuHandler = {
             element instanceof Ci.nsIDOMHTMLDivElement ||
             element instanceof Ci.nsIDOMHTMLLIElement ||
             element instanceof Ci.nsIDOMHTMLPreElement ||
             element instanceof Ci.nsIDOMHTMLHeadingElement ||
             element instanceof Ci.nsIDOMHTMLTableCellElement ||
             element instanceof Ci.nsIDOMHTMLBodyElement);
   },
 
+  _getLinkURL: function ch_getLinkURL(aLink) {
+    let href = aLink.href;
+    if (href)
+      return href;
+
+    href = aLink.getAttributeNS(kXLinkNamespace, "href");
+    if (!href || !href.match(/\S/)) {
+      // Without this we try to save as the current doc,
+      // for example, HTML case also throws if empty
+      throw "Empty href";
+    }
+
+    return Util.makeURLAbsolute(aLink.baseURI, href);
+  },
+
+  _getURI: function ch_getURI(aURL) {
+    try {
+      return Util.makeURI(aURL);
+    } catch (ex) { }
+
+    return null;
+  },
+
+  _getProtocol: function ch_getProtocol(aURI) {
+    if (aURI)
+      return aURI.scheme;
+    return null;
+  },
+
   /**
    * For add-ons to add new types and data to the ContextMenu message.
    *
    * @param aName A string to identify the new type.
    * @param aHandler A function that takes a state object and a target element.
    *    If aHandler returns true, then aName will be added to the list of types.
    *    The function may also modify the state object.
    */
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -214,17 +214,17 @@ var TouchModule = {
     // stop kinetic panning if targetScrollbox has changed
     if (this._kinetic.isActive() && this._dragger != dragger)
       this._kinetic.end();
 
     this._targetScrollbox = targetScrollInterface ? targetScrollInterface.element : targetScrollbox;
     this._targetScrollInterface = targetScrollInterface;
 
     if (!this._targetScrollbox) {
-      return false;
+      return;
     }
 
     // XXX shouldn't dragger always be valid here?
     if (dragger) {
       let draggable = dragger.isDraggable(targetScrollbox, targetScrollInterface);
       dragData.locked = !draggable.x || !draggable.y;
       if (draggable.x || draggable.y) {
         this._dragger = dragger;
--- a/browser/metro/base/content/sync.js
+++ b/browser/metro/base/content/sync.js
@@ -19,25 +19,20 @@ let WeaveGlue = {
     }
 
     let service = Components.classes["@mozilla.org/weave/service;1"]
                                     .getService(Components.interfaces.nsISupports)
                                     .wrappedJSObject;
 
     if (service.ready) {
       this._init();
-      return;
+    } else {
+      Services.obs.addObserver(this, "weave:service:ready", false);
+      service.ensureLoaded();
     }
-
-    Services.obs.addObserver(function onReady() {
-      Services.obs.removeObserver(onReady, "weave:service:ready");
-      this._init();
-    }.bind(this), "weave:service:ready", false);
-
-    service.ensureLoaded();
   },
 
   _init: function () {
     this._bundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
     this._msg = document.getElementById("prefs-messages");
 
     this._addListeners();
 
@@ -420,16 +415,22 @@ let WeaveGlue = {
     });
 
     // Replace the getter with the collection of settings
     delete this._elements;
     return this._elements = elements;
   },
 
   observe: function observe(aSubject, aTopic, aData) {
+    if (aTopic == "weave:service:ready") {
+      Services.obs.removeObserver(this, aTopic);
+      this._init();
+      return;
+    }
+
     // Make sure we're online when connecting/syncing
     Util.forceOnline();
 
     // Can't do anything before settings are loaded
     if (this._elements == null)
       return;
 
     // Make some aliases
--- a/browser/metro/base/tests/Makefile.in
+++ b/browser/metro/base/tests/Makefile.in
@@ -21,16 +21,18 @@ BROWSER_TESTS = \
   browser_remotetabs.js \
   browser_downloads.js \
   browser_plugin_input.html \
   browser_plugin_input_mouse.js \
   browser_plugin_input_keyboard.js \
   browser_context_menu_tests.js \
   browser_context_menu_tests_01.html \
   browser_context_menu_tests_02.html \
+  browser_context_menu_tests_03.html \
+  text-block.html \
   $(NULL)
 
 BROWSER_TEST_RESOURCES = \
   res\image01.png \
   $(NULL)
 
 libs:: $(BROWSER_TESTS)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/
--- a/browser/metro/base/tests/browser_context_menu_tests.js
+++ b/browser/metro/base/tests/browser_context_menu_tests.js
@@ -21,17 +21,16 @@ function debugClipFlavors(aClip)
 }
 
 // XXX won't work with out of process content
 function emptyClipboard() {
   Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
                                        .emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
 }
 
-// Image context menu tests
 gTests.push({
   desc: "text context menu",
   run: function test() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
     info(chromeRoot + "browser_context_menu_tests_02.html");
     yield addTab(chromeRoot + "browser_context_menu_tests_02.html");
 
@@ -392,11 +391,124 @@ gTests.push({
     let imagetab = Browser.getTabFromChrome(event.originalTarget);
     ok(imagetab != null, "tab created");
     ok(imagetab.browser.currentURI.spec == "chrome://mochitests/content/metro/res/image01.png", "tab location");
 
     Browser.closeTab(imagetab);
   }
 });
 
+gTests.push({
+  desc: "tests for subframe positioning",
+  run: function test() {
+    info(chromeRoot + "browser_context_menu_tests_03.html");
+    yield addTab(chromeRoot + "browser_context_menu_tests_03.html");
+
+    let win = Browser.selectedTab.browser.contentWindow;
+
+    // Sometimes the context ui is visible, sometimes it isn't.
+    try {
+      yield waitForCondition(function () {
+        return ContextUI.isVisible;  
+      }, 500, 50);
+    } catch (ex) {}
+
+    ContextUI.dismiss();
+
+    let frame1 = win.document.getElementById("frame1");
+    let link1 = frame1.contentDocument.getElementById("link1");
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(frame1.contentDocument.defaultView, link1, 85, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    ok(ContextMenuUI._panel.left > 375 && ContextMenuUI._panel.left < 390, "position");
+    ok(ContextMenuUI._panel.top > 235 && ContextMenuUI._panel.top < 245, "position");
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    frame1.contentDocument.defaultView.scrollBy(0, 200);
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(frame1.contentDocument.defaultView, link1, 85, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    ok(ContextMenuUI._panel.left > 375 && ContextMenuUI._panel.left < 390, "position");
+    ok(ContextMenuUI._panel.top > 35 && ContextMenuUI._panel.top < 45, "position");
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    let rlink1 = win.document.getElementById("rlink1");
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, rlink1, 40, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    ok(ContextMenuUI._panel.left > 730 && ContextMenuUI._panel.left < 745, "position");
+    ok(ContextMenuUI._panel.top > 600 && ContextMenuUI._panel.top < 610, "position");
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    win.scrollBy(0, 200);
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, rlink1, 40, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    ok(ContextMenuUI._panel.left > 730 && ContextMenuUI._panel.left < 745, "position");
+    ok(ContextMenuUI._panel.top > 400 && ContextMenuUI._panel.top < 410, "position");
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    let link2 = frame1.contentDocument.getElementById("link2");
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(frame1.contentDocument.defaultView, link2, 85, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    info(ContextMenuUI._panel.left);
+    info(ContextMenuUI._panel.top);
+
+    ok(ContextMenuUI._panel.left > 380 && ContextMenuUI._panel.left < 390, "position");
+    ok(ContextMenuUI._panel.top > 170 && ContextMenuUI._panel.top < 185, "position");
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+  }
+});
+
 function test() {
   runTests();
 }
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/browser_context_menu_tests_03.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="utf-8">
+</head>
+
+<body  bgcolor=white>
+
+Hello there. <a href="#hello">hello there.</a>
+<center>
+
+<iframe id="frame1" width="800" height="600" src="text-block.html"></iframe>
+
+<br />
+<br />
+Hello there. <a id="rlink1" href="#hello">hello there.</a>
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+
+</center>
+</body></html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/text-block.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+</head>
+<body>
+<div id="content">
+    <p>(start of paragraph)
+    Alice was beginning to get very (break)<br>
+    tired of sitting by her sister on the bank, and of having nothing to do: once or twice she (span)
+    <span>(start of span) had peeped into the book her sister was reading (end of span)</span>,
+    but it had no pictures or conversations in it, `and what is the use of a book,' thought Alice
+    `without pictures or conversation?' (break)<br> 
+    (end of paragraph)</p>
+    (in between paragraphs)
+    <p>(start of paragraph)
+    Alice was beginning to get very
+    tired of sitting by her sister on the bank, and of having nothing to do: once or twice she
+    had peeped into the book her sister was reading, but it had no pictures or conversations
+    in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'(break)<br>
+    (end of paragraph)</p>
+
+    <p><a id="link1" href="#hello">(start of paragraph)</a>
+    So she was considering in her own mind (as well as she could, for the hot day made her
+    feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth
+    the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink
+    eyes ran close by her. <br>
+    </p>
+
+    <p>
+    There was nothing so VERY remarkable in that; nor did Alice think it so VERY much out of
+    the way to hear the Rabbit say to itself, `Oh dear! Oh dear! I shall be late!' (when she
+    thought it over afterwards, it occurred to her that she ought to have wondered at this,
+    but at the time it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
+    OUT OF ITS WAISTCOAT- POCKET, and looked at it, and then hurried on, Alice started to her
+    feet, for it flashed across her mind that she had never before seen a rabbit with either a
+    waistcoat-pocket, or a watch to take out of it, and burning with curiosity, she ran across
+    the field after it, and fortunately was just in time to see it pop down a large
+    rabbit-hole under the hedge. <br>
+    <br>
+    In another moment down went Alice after it, never once considering how in the world she
+    was to get out again. <br>
+    <br>
+    The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly
+    down, so suddenly that Alice had not a moment to think about stopping herself before she
+    found herself falling down a very deep well. <br>
+    <br><a id="link2" href="#hello">(start of paragraph)</a>
+    Either the well was very deep, or she fell very slowly, for she had plenty of time as she
+    went down to look about her and to wonder what was going to happen next. First, she tried
+    to look down and make out what she was coming to, but it was too dark to see anything;
+    then she looked at the sides of the well, and noticed that they were filled with cupboards
+    and book-shelves; here and there she saw maps and pictures hung upon pegs. She took down a
+    jar from one of the shelves as she passed; it was labelled `ORANGE MARMALADE', but to her
+    great disappointment it was empty: she did not like to drop the jar for fear of killing
+    somebody, so managed to put it into one of the cupboards as she fell past it. <br>
+    <br>
+    `Well!' thought Alice to herself, `after such a fall as this, I shall think nothing of
+    tumbling down stairs! How brave they'll all think me at home! Why, I wouldn't say anything
+    about it, even if I fell off the top of the house!' (Which was very likely true.) <br>
+    <br>
+    Down, down, down.
+    <br>
+</div>
+</body></html>
\ No newline at end of file
--- a/browser/metro/shell/Makefile.in
+++ b/browser/metro/shell/Makefile.in
@@ -14,20 +14,13 @@ include $(DEPTH)/config/autoconf.mk
 DIRS = commandexecutehandler linktool
 
 ifdef ENABLE_TESTS
 DIRS += testing
 endif
 
 export::
 	$(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin
-	$(RM) $(DIST)/bin/VisualElementsManifest.xml
-	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} \
-	  $(srcdir)/VisualElementsManifest.xml.in > $(DIST)/bin/VisualElementsManifest.xml
 
 install::
 	$(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin
 
-# bug 744566
-#	$(RM) $(DIST)/bin/resources.pri
-# 	$(MAKEPRI) new -v -pr $(srcdir)/tileresources -cf $(srcdir)/priconfig.xml -mn $(srcdir)/AppManifest.xml -of $(DIST)/bin/resources.pri -o
-
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/metro/shell/VisualElementsManifest.xml.in
+++ /dev/null
@@ -1,16 +0,0 @@
-<Application
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <VisualElements
-      DisplayName="@MOZ_APP_DISPLAYNAME@"
-      Logo="tileresources\VisualElements_logo.png"
-      SmallLogo="tileresources\VisualElements_smalllogo.png"
-      ForegroundText="light"
-      BackgroundColor="#1895f5">
-    <DefaultTile
-        ShortName="@MOZ_APP_DISPLAYNAME@"
-        ShowName="allLogos"
-            />
-    <SplashScreen
-        Image="tileresources\VisualElements_splashscreen.png" />
-  </VisualElements>
-</Application>
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -52,23 +52,22 @@ this.Social = {
                                    Ci.nsISupportsString, string);
   },
 
   _provider: null,
   get provider() {
     return this._provider;
   },
   set provider(val) {
-    // Changes triggered by the public setter should notify of an engine change.
-    this._setProvider(val, true);
+    this._setProvider(val);
   },
 
   // Sets the current provider and enables it. Also disables the
-  // previously set provider, and optionally notifies observers of the change.
-  _setProvider: function (provider, notify) {
+  // previously set provider, and notifies observers of the change.
+  _setProvider: function (provider) {
     if (this._provider == provider)
       return;
 
     // Disable the previous provider, if any, since we want only one provider to
     // be enabled at once.
     if (this._provider)
       this._provider.enabled = false;
 
@@ -79,64 +78,58 @@ this.Social = {
       this._currentProviderPref = this._provider.origin;
     }
     let enabled = !!provider;
     if (enabled != SocialService.enabled) {
       SocialService.enabled = enabled;
       Services.prefs.setBoolPref("social.enabled", enabled);
     }
 
-    if (notify) {
-      let origin = this._provider && this._provider.origin;
-      Services.obs.notifyObservers(null, "social:provider-set", origin);
-    }
+    let origin = this._provider && this._provider.origin;
+    Services.obs.notifyObservers(null, "social:provider-set", origin);
   },
 
   get defaultProvider() {
     if (this.providers.length == 0)
       return null;
     let provider = this._getProviderFromOrigin(this._currentProviderPref);
     return provider || this.providers[0];
   },
 
-  init: function Social_init(callback) {
+  init: function Social_init() {
     this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled;
 
     if (this.providers) {
-      schedule(callback);
       return;
     }
 
     // Retrieve the current set of providers, and set the current provider.
     SocialService.getProviderList(function (providers) {
-      // We don't want to notify about a provider change when we're setting
-      // this.provider for the first time, so pass false here.
-      this._updateProviderCache(providers, false);
-      callback();
+      this._updateProviderCache(providers);
     }.bind(this));
 
     // Register an observer for changes to the provider list
     SocialService.registerProviderListener(function providerListener(topic, data) {
       // An engine change caused by adding/removing a provider should notify
       if (topic == "provider-added" || topic == "provider-removed") {
-        this._updateProviderCache(data, true);
+        this._updateProviderCache(data);
         Services.obs.notifyObservers(null, "social:providers-changed", null);
       }
     }.bind(this));
   },
 
   // Called to update our cache of providers and set the current provider
-  _updateProviderCache: function (providers, notifyProviderChange) {
+  _updateProviderCache: function (providers) {
     this.providers = providers;
 
     // If social is currently disabled there's nothing else to do.
     if (!SocialService.enabled)
       return;
     // Otherwise set the provider.
-    this._setProvider(this.defaultProvider, notifyProviderChange);
+    this._setProvider(this.defaultProvider);
   },
 
   set enabled(val) {
     // Setting .enabled is just a shortcut for setting the provider to either
     // the default provider or null...
     if (val) {
       if (!this.provider)
         this.provider = this.defaultProvider;
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -89,16 +89,25 @@ if test "$CLANG_CXX"; then
     ## from C.
     ##
     ## mismatched-tags is disabled (bug 780474) mostly because it's useless.
     ## Worse, it's not supported by gcc, so it will cause tryserver bustage
     ## without any easy way for non-Clang users to check for it.
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-mismatched-tags"
 fi
 
+if test -z "$GNU_CC"; then
+    case "$target" in
+    *-mingw*)
+        ## Warning 4099 (equivalent of mismatched-tags) is disabled (bug 780474)
+        ## for the same reasons as above.
+        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -wd4099"
+    esac
+fi
+
 if test "$GNU_CC"; then
     CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
     CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-exceptions"
 fi
 
 dnl ========================================================
 dnl = Identical Code Folding
 dnl ========================================================
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -820,16 +820,24 @@ user_pref("camino.use_system_proxy_setti
       env['MOZ_CRASHREPORTER'] = '1'
     else:
       env['MOZ_CRASHREPORTER_DISABLE'] = '1'
 
     env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
     env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
 
+    # Additional temporary logging while we try to debug some intermittent
+    # WebRTC conditions. This is necessary to troubleshoot bugs 841496,
+    # 841150, and 839677 (at least)
+    env['NSPR_LOG_MODULES'] = 'signaling:3,mtransport:3'
+    env['R_LOG_LEVEL'] = '5'
+    env['R_LOG_DESTINATION'] = 'stderr'
+    env['R_LOG_VERBOSE'] = '1'
+
     # ASan specific environment stuff
     if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC):
       try:
         totalMemory = int(os.popen("free").readlines()[1].split()[1])
 
         # Only 2 GB RAM or less available? Use custom ASan options to reduce
         # the amount of resources required to do the tests. Standard options 
         # will otherwise lead to OOM conditions on the current test slaves.
--- a/build/virtualenv/packages.txt
+++ b/build/virtualenv/packages.txt
@@ -2,14 +2,16 @@ simplejson.pth:python/simplejson-2.1.1
 marionette.pth:testing/marionette/client
 blessings.pth:python/blessings
 mach.pth:python/mach
 mozbuild.pth:python/mozbuild
 pymake.pth:build/pymake
 optional:setup.py:python/psutil:build_ext:--inplace
 optional:psutil.pth:python/psutil
 which.pth:python/which
+ply.pth:other-licenses/ply/
+codegen.pth:python/codegen/
 mock.pth:python/mock-1.0.0
 mozilla.pth:build
 mozilla.pth:config
 mozilla.pth:xpcom/typelib/xpt/tools
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
@@ -17,16 +17,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript;version=1.7">
 
 /** Test for Bug 758258 **/
 
 var Ci = Components.interfaces;
 
+if (navigator.platform.indexOf("Linux") == 0) {
+  SimpleTest.expectAssertions(2);
+}
+
 SimpleTest.waitForExplicitFinish();
 
 /*
  * gData is an array of objects. Each object represents a test case.
  * - app: gives the app manifest URL, will set mozapp to it on the iframe;
  * - origin: gives the origin of the iframe. This is the URL thas is going to
  *           to be passed as iframe.src but also the expected principal's
  *           origin.
--- a/configure.in
+++ b/configure.in
@@ -2152,18 +2152,19 @@ ia64*-hpux*)
         # name of the enum itself.  This behavior is allowed in C++11, and the
         # warning has been removed in VS2012.
         # MSVC warning C4800 warns when a value is implicitly cast to bool,
         # because this also forces narrowing to a single byte, which can be a
         # perf hit.  But this matters so little in practice (and often we want
         # that behavior) that it's better to turn it off.
         # MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign)
         # on non-Western system locales even if it is in a comment.
+        # khuey says we can safely ignore MSVC warning C4251
         CFLAGS="$CFLAGS -wd4819"
-        CXXFLAGS="$CXXFLAGS -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
+        CXXFLAGS="$CXXFLAGS -wd4251 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
         # make 'foo == bar;' error out
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib secur32.lib netapi32.lib"
         MOZ_DEBUG_FLAGS='-Zi'
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS='-O1'
@@ -4667,47 +4668,50 @@ dnl ====================================
 if test "$MOZ_ENABLE_QT"
 then
     MOZ_ARG_WITH_STRING(qtdir,
     [  --with-qtdir=\$dir       Specify Qt directory ],
     [ QTDIR=$withval])
 
     if test -z "$QTDIR"; then
         PKG_CHECK_MODULES(MOZ_QT, QtGui QtNetwork QtCore QtOpenGL)
-        PKG_CHECK_MODULES(MOZ_QT5, QtWidgets QtMultimedia QtPrintSupport,
+        PKG_CHECK_MODULES(MOZ_QT5, Qt5Widgets Qt5Multimedia Qt5PrintSupport,
                       MOZ_ENABLE_QT5=1,
                       MOZ_ENABLE_QT5=)
         if test "$MOZ_ENABLE_QT5"; then
             echo "Using qt5"
             MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS $MOZ_QT5_CFLAGS"
             MOZ_QT_LIBS="$MOZ_QT_LIBS $MOZ_QT5_LIBS"
         fi
 
         AC_CHECK_PROGS(HOST_MOC, $MOC moc, "")
         AC_CHECK_PROGS(HOST_RCC, $RCC rcc, "")
     else
-        MOZ_QT_LIBS="-L$QTDIR/lib/ -lQtGui -lQtNetwork -lQtCore -lQtXml -lQtOpenGL"
-
         MOZ_QT_CFLAGS="-DQT_SHARED"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include"
-        MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/Qt"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtGui"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtCore"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtNetwork"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtXml"
         MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtDeclarative"
-        HOST_MOC="$QTDIR/bin/moc"
-        HOST_RCC="$QTDIR/bin/rcc"
 
         # QtWidgets was introduced only in Qt5
         if test -d $QTDIR/include/QtWidgets; then
+            echo "Using qt5"
+            MOZ_QT_LIBS="-L$QTDIR/lib/ -lQt5Gui -lQt5Network -lQt5Core -lQt5Xml -lQt5OpenGL"
+            MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtGui/5.0.1/QtGui"
             MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtWidgets"
             MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/QtPrintSupport"
-            MOZ_QT_LIBS="$MOZ_QT_LIBS -lQtWidgets -lQtPrintSupport"
+            MOZ_QT_LIBS="$MOZ_QT_LIBS -lQt5Widgets -lQt5PrintSupport"
+        else
+            MOZ_QT_LIBS="-L$QTDIR/lib/ -lQtGui -lQtNetwork -lQtCore -lQtXml -lQtOpenGL"
+            MOZ_QT_CFLAGS="$MOZ_QT_CFLAGS -I$QTDIR/include/Qt"
         fi
+        HOST_MOC="$QTDIR/bin/moc"
+        HOST_RCC="$QTDIR/bin/rcc"
     fi
     if test -z "$HOST_MOC"; then
         AC_MSG_ERROR([No acceptable moc preprocessor found. Qt SDK is not installed or --with-qt is
 incorrect])
     fi
     if test -z "$HOST_RCC"; then
         AC_MSG_ERROR([No acceptable rcc preprocessor found. Qt SDK is not installed or --with-qt is
 incorrect])
@@ -8966,30 +8970,33 @@ dnl Load the list of Makefiles to genera
 dnl   To add new Makefiles, edit allmakefiles.sh.
 dnl   allmakefiles.sh sets the variable, MAKEFILES.
 . ${srcdir}/allmakefiles.sh
 
 echo $MAKEFILES > unallmakefiles
 
 AC_OUTPUT($MAKEFILES)
 
-# target_arch is from {ia32|x64|arm}
+# target_arch is from {ia32|x64|arm|ppc}
 case "$CPU_ARCH" in
 x86_64 | ia64)
     WEBRTC_TARGET_ARCH=x64
     ;;
 
 arm*)
     WEBRTC_TARGET_ARCH=arm
     ;;
 
 x86)
     WEBRTC_TARGET_ARCH=ia32
     ;;
 
+ppc*)
+    WEBRTC_TARGET_ARCH=ppc
+    ;;
 *)
 # unsupported arch for webrtc
     WEBRTC_TARGET_ARCH=unknown
     MOZ_WEBRTC=
     ;;
 
 esac
 
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -97,18 +97,18 @@ class HTMLBodyElement;
 class Link;
 class ProcessingInstruction;
 class UndoManager;
 template<typename> class Sequence;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x4e6f7d97, 0x091e, 0x4eda, \
-  { 0xb7, 0xd6, 0xfe, 0xb0, 0xb8, 0x01, 0x2a, 0x93 } }
+{ 0x45ce048f, 0x5970, 0x411e, \
+  { 0xaa, 0x99, 0x12, 0xed, 0x3a, 0x55, 0xc9, 0xc3 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -881,17 +881,17 @@ public:
   virtual nsresult RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
                                                 const nsAString& aNewOrigin) = 0;
 
   /**
    * Called when a frame in a remote child document has rolled back fullscreen
    * so that all its fullscreen element stacks are empty; we must continue the
    * rollback in this parent process' doc tree branch which is fullscreen.
    * Note that only one branch of the document tree can have its documents in
-   * fullscreen state at one time. We're in inconsistent state if the a 
+   * fullscreen state at one time. We're in inconsistent state if a
    * fullscreen document has a parent and that parent isn't fullscreen. We
    * preserve this property across process boundaries.
    */
    virtual nsresult RemoteFrameFullscreenReverted() = 0;
 
   /**
    * Restores the previous full-screen element to full-screen status. If there
    * is no former full-screen element, this exits full-screen, moving the
@@ -900,30 +900,51 @@ public:
   virtual void RestorePreviousFullScreenState() = 0;
 
   /**
    * Returns true if this document is in full-screen mode.
    */
   virtual bool IsFullScreenDoc() = 0;
 
   /**
+   * Returns true if this document is a fullscreen leaf document, i.e. it
+   * is in fullscreen mode and has no fullscreen children.
+   */
+  virtual bool IsFullscreenLeaf() = 0;
+
+  /**
+   * Returns the document which is at the root of this document's branch
+   * in the in-process document tree. Returns nullptr if the document isn't
+   * fullscreen.
+   */
+  virtual nsIDocument* GetFullscreenRoot() = 0;
+
+  /**
+   * Sets the fullscreen root to aRoot. This stores a weak reference to aRoot
+   * in this document.
+   */
+  virtual void SetFullscreenRoot(nsIDocument* aRoot) = 0;
+
+  /**
    * Sets whether this document is approved for fullscreen mode.
    * Documents aren't approved for fullscreen until chrome has sent a
    * "fullscreen-approved" notification with a subject which is a pointer
    * to the approved document.
    */
   virtual void SetApprovedForFullscreen(bool aIsApproved) = 0;
 
   /**
-   * Exits all documents from DOM full-screen mode, and moves the top-level
-   * browser window out of full-screen mode. If aRunAsync is true, this runs
-   * asynchronously.
+   * Exits documents out of DOM fullscreen mode. If aDocument is non null,
+   * only its ancestors and descendants exit fullscreen, i.e. if there are
+   * multiple windows/doctrees in fullscreen mode, only the one containing
+   * aDocument exits fullscreen mode. If aDocument is null, all windows
+   * and documents exit fullscreen. If aRunAsync is true, fullscreen is
+   * executed asynchronously.
    */
-  static void ExitFullScreen(bool aRunAsync);
-
+  static void ExitFullscreen(nsIDocument* aDocument, bool aRunAsync);
 
   virtual void RequestPointerLock(Element* aElement) = 0;
 
   static void UnlockPointer();
 
 
   //----------------------------------------------------------------------
 
--- a/content/base/public/nsIImageLoadingContent.idl
+++ b/content/base/public/nsIImageLoadingContent.idl
@@ -32,17 +32,17 @@ interface nsIFrame;
  * missed.  We should NOT freeze this interface without considering
  * this issue.  (It could be that the image status on imgIRequest is
  * sufficient, when combined with the imageBlockingStatus information.)
  *
  * Please make sure to update the HTMLImageElement and SVGImageElement
  * Web IDL interfaces to mirror this interface when changing it.
  */
 
-[scriptable, builtinclass, uuid(497bfb9b-d996-4d1e-a647-8137b0cfc876)]
+[scriptable, builtinclass, uuid(e3968acd-b796-4ca3-aec0-e7f0880f2219)]
 interface nsIImageLoadingContent : imgINotificationObserver
 {
   /**
    * Request types.  Image loading content nodes attempt to do atomic
    * image changes when the image url is changed.  This means that
    * when the url changes the new image load will start, but the old
    * image will remain the "current" request until the new image is
    * fully loaded.  At that point, the old "current" request will be
@@ -154,9 +154,17 @@ interface nsIImageLoadingContent : imgIN
   void forceReload();
 
   /**
    * Enables/disables image state forcing. When |aForce| is PR_TRUE, we force
    * nsImageLoadingContent::ImageState() to return |aState|. Call again with |aForce|
    * as PR_FALSE to revert ImageState() to its original behaviour.
    */
   void forceImageState(in boolean aForce, in unsigned long long aState);
+
+  /**
+   * A visible count is stored, if it is non-zero then this image is considered
+   * visible. These methods increment, decrement, or return the visible coount.
+   */
+  [noscript, notxpcom] void IncrementVisibleCount();
+  [noscript, notxpcom] void DecrementVisibleCount();
+  [noscript, notxpcom] uint32_t GetVisibleCount();
 };
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -1293,17 +1293,17 @@ Element::UnbindFromTree(bool aDeep, bool
     if (IsFullScreenAncestor()) {
       // The element being removed is an ancestor of the full-screen element,
       // exit full-screen state.
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       "DOM", OwnerDoc(),
                                       nsContentUtils::eDOM_PROPERTIES,
                                       "RemovedFullScreenElement");
       // Fully exit full-screen.
-      nsIDocument::ExitFullScreen(false);
+      nsIDocument::ExitFullscreen(OwnerDoc(), /* async */ false);
     }
     if (HasPointerLock()) {
       nsIDocument::UnlockPointer();
     }
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -190,23 +190,16 @@
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsViewportInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
-// Reference to the document which requested DOM full-screen mode.
-nsWeakPtr nsDocument::sFullScreenDoc = nullptr;
-
-// Reference to the root document of the branch containing the document
-// which requested DOM full-screen mode.
-nsWeakPtr nsDocument::sFullScreenRootDoc = nullptr;
-
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
@@ -7777,28 +7770,36 @@ nsDocument::OnPageHide(bool aPersisted,
   mVisible = false;
 
   UpdateVisibilityState();
 
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
   EnumerateFreezableElements(NotifyActivityChanged, nullptr);
 
   if (IsFullScreenDoc()) {
-    // A full-screen doc has been hidden. We need to ensure we exit
-    // full-screen, i.e. remove full-screen state from all full-screen
-    // documents, and exit the top-level window from full-screen mode.
-    // By the time a doc is hidden, it has been removed from the doc tree,
-    // so nsIDocument::ExitFullScreen() won't be able to traverse to this
-    // document to reset its state, so reset full-screen state in *this*
-    // document. OnPageHide() is called in every hidden document, so doing
-    // this ensures all hidden documents have their full-screen state reset.
+    // If this document was fullscreen, we should exit fullscreen in this
+    // doctree branch. This ensures that if the user navigates while in
+    // fullscreen mode we don't leave its still visible ancestor documents
+    // in fullscreen mode. So exit fullscreen in the document's fullscreen
+    // root document, as this will exit fullscreen in all the root's
+    // descendant documents. Note that documents are removed from the
+    // doctree by the time OnPageHide() is called, so we must store a
+    // reference to the root (in nsDocument::mFullscreenRoot) since we can't
+    // just traverse the doctree to get the root.
+    nsIDocument::ExitFullscreen(this, /* async */ false);
+
+    // Since the document is removed from the doctree before OnPageHide() is
+    // called, ExitFullscreen() can't traverse from the root down to *this*
+    // document, so we must manually call CleanupFullscreenState() below too.
+    // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
+    // so we *must* call it after ExitFullscreen(), not before.
+    // OnPageHide() is called in every hidden (i.e. descendant) document,
+    // so calling CleanupFullscreenState() here will ensure all hidden
+    // documents have their fullscreen state reset.
     CleanupFullscreenState();
-
-    // Next reset full-screen state in all visible documents in the doctree.
-    nsIDocument::ExitFullScreen(false);
   }
 }
 
 void
 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
 {
   NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
                mSubtreeModifiedTargets.Count() == 0,
@@ -8961,146 +8962,16 @@ nsIDocument::CreateTouchList(const Seque
 {
   nsRefPtr<nsDOMTouchList> retval = new nsDOMTouchList();
   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
     retval->Append(aTouches[i]);
   }
   return retval.forget();
 }
 
-static void
-DispatchFullScreenChange(nsIDocument* aTarget)
-{
-  nsRefPtr<nsAsyncDOMEvent> e =
-    new nsAsyncDOMEvent(aTarget,
-                        NS_LITERAL_STRING("mozfullscreenchange"),
-                        true,
-                        false);
-  e->PostDOMEvent();
-}
-
-NS_IMETHODIMP
-nsDocument::MozCancelFullScreen()
-{
-  nsIDocument::MozCancelFullScreen();
-  return NS_OK;
-}
-
-void
-nsIDocument::MozCancelFullScreen()
-{
-  // Only perform fullscreen changes if we're running in a webapp
-  // same-origin to the web app, or if we're in a user generated event
-  // handler.
-  if (NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
-      nsContentUtils::IsRequestFullScreenAllowed()) {
-    RestorePreviousFullScreenState();
-  }
-}
-
-// Runnable to set window full-screen mode. Used as a script runner
-// to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
-// run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
-// (handled in chome code) which is unsafe to run if this is called in
-// Element::UnbindFromTree().
-class nsSetWindowFullScreen : public nsRunnable {
-public:
-  nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
-    : mDoc(aDoc), mValue(aValue) {}
-
-  NS_IMETHOD Run()
-  {
-    if (mDoc->GetWindow()) {
-      mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsCOMPtr<nsIDocument> mDoc;
-  bool mValue;
-};
-
-static void
-SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
-{
-  nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
-}
-
-class nsCallExitFullScreen : public nsRunnable {
-public:
-  NS_IMETHOD Run()
-  {
-    nsDocument::ExitFullScreen();
-    return NS_OK;
-  }
-};
-
-/* static */
-void
-nsIDocument::ExitFullScreen(bool aRunAsync)
-{
-  if (aRunAsync) {
-    NS_DispatchToCurrentThread(new nsCallExitFullScreen());
-    return;
-  }
-  nsDocument::ExitFullScreen();
-}
-
-// Returns true if the document is a direct child of a cross process parent
-// mozbrowser iframe. This is the case when the document has a null parent,
-// and its DocShell reports that it is a browser frame.
-static bool
-HasCrossProcessParent(nsIDocument* aDocument)
-{
-  if (XRE_GetProcessType() != GeckoProcessType_Content) {
-    return false;
-  }
-  if (aDocument->GetParentDocument() != nullptr) {
-    return false;
-  }
-  nsPIDOMWindow* win = aDocument->GetWindow();
-  if (!win) {
-    return false;
-  }
-  nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
-  if (!docShell) {
-    return false;
-  }
-  return docShell->GetIsBrowserOrApp();
-}
-
-static bool
-ResetFullScreen(nsIDocument* aDocument, void* aData)
-{
-  if (aDocument->IsFullScreenDoc()) {
-    static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
-    NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
-    nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
-    changed->AppendElement(aDocument);
-
-    if (HasCrossProcessParent(aDocument)) {
-      // We're at the top of the content-process side doc tree. Ask the parent
-      // process to exit fullscreen.
-      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-      os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
-    }
-
-    // Dispatch a notification so that if this document has any
-    // cross-process subdocuments, they'll be notified to exit fullscreen.
-    // The BrowserElementParent listens for this event and performs the
-    // cross process notification if it has a remote child process.
-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-    os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
-
-    aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
-  }
-  return true;
-}
-
 already_AddRefed<nsDOMCaretPosition>
 nsIDocument::CaretPositionFromPoint(float aX, float aY)
 {
   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
   nsPoint pt(x, y);
 
   nsIPresShell *ps = GetShell();
@@ -9153,81 +9024,450 @@ nsIDocument::CaretPositionFromPoint(floa
 NS_IMETHODIMP
 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
 {
   NS_ENSURE_ARG_POINTER(aCaretPos);
   *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).get();
   return NS_OK;
 }
 
+namespace mozilla {
+
+// Singleton class to manage the list of fullscreen documents which are the
+// root of a branch which contains fullscreen documents. We maintain this list
+// so that we can easily exit all windows from fullscreen when the user
+// presses the escape key.
+class FullscreenRoots {
+public:
+  // Adds a root to the manager. Adding a root multiple times does not result
+  // in duplicate entries for that item, only one.
+  static void Add(nsIDocument* aRoot);
+
+  // Iterates over every root in the root list, and calls aFunction, passing
+  // each root once to aFunction. It is safe to call Add() and Remove() while
+  // iterating over the list (i.e. in aFunction). Documents that are removed
+  // from the manager during traversal are not traversed, and documents that
+  // are added to the manager during traversal are also not traversed.
+  static void ForEach(void(*aFunction)(nsIDocument* aDoc));
+
+  // Removes a specific root from the manager.
+  static void Remove(nsIDocument* aRoot);
+
+  // Returns true if all roots added to the list have been removed.
+  static bool IsEmpty();
+
+private:
+
+  FullscreenRoots() {
+    MOZ_COUNT_CTOR(FullscreenRoots);
+  }
+  ~FullscreenRoots() {
+    MOZ_COUNT_DTOR(FullscreenRoots);
+  }
+
+  enum {
+    NotFound = uint32_t(-1)
+  };
+  // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
+  static uint32_t Find(nsIDocument* aRoot);
+
+  // Returns true if aRoot is in the list of fullscreen roots.
+  static bool Contains(nsIDocument* aRoot);
+
+  // Singleton instance of the FullscreenRoots. This is instantiated when a
+  // root is added, and it is deleted when the last root is removed.
+  static FullscreenRoots* sInstance;
+
+  // List of weak pointers to roots.
+  nsTArray<nsWeakPtr> mRoots;
+};
+
+FullscreenRoots* FullscreenRoots::sInstance = nullptr;
+
 /* static */
 void
-nsDocument::ExitFullScreen()
-{
-  // Clear full-screen stacks in all descendant documents.
-  nsCOMPtr<nsIDocument> root(do_QueryReferent(sFullScreenRootDoc));
+FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
+{
+  if (!sInstance) {
+    return;
+  }
+  // Create a copy of the roots array, and iterate over the copy. This is so
+  // that if an element is removed from mRoots we don't mess up our iteration.
+  nsTArray<nsWeakPtr> roots(sInstance->mRoots);
+  // Call aFunction on all entries.
+  for (uint32_t i = 0; i < roots.Length(); i++) {
+    nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
+    // Check that the root isn't in the manager. This is so that new additions
+    // while we were running don't get traversed.
+    if (root && FullscreenRoots::Contains(root)) {
+      aFunction(root);
+    }
+  }
+}
+
+/* static */
+bool
+FullscreenRoots::Contains(nsIDocument* aRoot)
+{
+  return FullscreenRoots::Find(aRoot) != NotFound;
+}
+
+/* static */
+void
+FullscreenRoots::Add(nsIDocument* aRoot)
+{
+  if (!FullscreenRoots::Contains(aRoot)) {
+    if (!sInstance) {
+      sInstance = new FullscreenRoots();
+    }
+    sInstance->mRoots.AppendElement(do_GetWeakReference(aRoot));
+  }
+}
+
+/* static */
+uint32_t
+FullscreenRoots::Find(nsIDocument* aRoot)
+{
+  if (!sInstance) {
+    return NotFound;
+  }
+  nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
+  for (uint32_t i = 0; i < roots.Length(); i++) {
+    nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
+    if (otherRoot == aRoot) {
+      return i;
+    }
+  }
+  return NotFound;
+}
+
+/* static */
+void
+FullscreenRoots::Remove(nsIDocument* aRoot)
+{
+  uint32_t index = Find(aRoot);
+  NS_ASSERTION(index != NotFound,
+    "Should only try to remove roots which are still added!");
+  if (index == NotFound || !sInstance) {
+    return;
+  }
+  sInstance->mRoots.RemoveElementAt(index);
+  if (sInstance->mRoots.IsEmpty()) {
+    delete sInstance;
+    sInstance = nullptr;
+  }
+}
+
+/* static */
+bool
+FullscreenRoots::IsEmpty()
+{
+  return !sInstance;
+}
+
+} // end namespace mozilla.
+using mozilla::FullscreenRoots;
+
+nsIDocument*
+nsDocument::GetFullscreenRoot()
+{
+  nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
+  return root;
+}
+
+void
+nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
+{
+  mFullscreenRoot = do_GetWeakReference(aRoot);
+}
+
+static void
+DispatchFullScreenChange(nsIDocument* aTarget)
+{
+  nsRefPtr<nsAsyncDOMEvent> e =
+    new nsAsyncDOMEvent(aTarget,
+                        NS_LITERAL_STRING("mozfullscreenchange"),
+                        true,
+                        false);
+  e->PostDOMEvent();
+}
+
+NS_IMETHODIMP
+nsDocument::MozCancelFullScreen()
+{
+  nsIDocument::MozCancelFullScreen();
+  return NS_OK;
+}
+
+void
+nsIDocument::MozCancelFullScreen()
+{
+  // Only perform fullscreen changes if we're running in a webapp
+  // same-origin to the web app, or if we're in a user generated event
+  // handler.
+  if (NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
+      nsContentUtils::IsRequestFullScreenAllowed()) {
+    RestorePreviousFullScreenState();
+  }
+}
+
+// Runnable to set window full-screen mode. Used as a script runner
+// to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
+// run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
+// (handled in chome code) which is unsafe to run if this is called in
+// Element::UnbindFromTree().
+class nsSetWindowFullScreen : public nsRunnable {
+public:
+  nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
+    : mDoc(aDoc), mValue(aValue) {}
+
+  NS_IMETHOD Run()
+  {
+    if (mDoc->GetWindow()) {
+      mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIDocument> mDoc;
+  bool mValue;
+};
+
+static void
+SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
+{
+  // Maintain list of fullscreen root documents.
+  nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
+  if (aValue) {
+    FullscreenRoots::Add(root);
+  } else {
+    FullscreenRoots::Remove(root);
+  }
+  nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
+}
+
+class nsCallExitFullscreen : public nsRunnable {
+public:
+  nsCallExitFullscreen(nsIDocument* aDoc)
+    : mDoc(aDoc) {}
+  NS_IMETHOD Run()
+  {
+    nsDocument::ExitFullscreen(mDoc);
+    return NS_OK;
+  }
+private:
+  nsCOMPtr<nsIDocument> mDoc;
+};
+
+/* static */
+void
+nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
+{
+  if (aDoc && !aDoc->IsFullScreenDoc()) {
+    return;
+  }
+  if (aRunAsync) {
+    NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
+    return;
+  }
+  nsDocument::ExitFullscreen(aDoc);
+}
+
+// Returns true if the document is a direct child of a cross process parent
+// mozbrowser iframe. This is the case when the document has a null parent,
+// and its DocShell reports that it is a browser frame.
+static bool
+HasCrossProcessParent(nsIDocument* aDocument)
+{
+  if (XRE_GetProcessType() != GeckoProcessType_Content) {
+    return false;
+  }
+  if (aDocument->GetParentDocument() != nullptr) {
+    return false;
+  }
+  nsPIDOMWindow* win = aDocument->GetWindow();
+  if (!win) {
+    return false;
+  }
+  nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
+  if (!docShell) {
+    return false;
+  }
+  return docShell->GetIsBrowserOrApp();
+}
+
+static bool
+CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
+{
+  if (aDoc->IsFullScreenDoc()) {
+    uint32_t* count = static_cast<uint32_t*>(aData);
+    (*count)++;
+  }
+  return true;
+}
+
+static uint32_t
+CountFullscreenSubDocuments(nsIDocument* aDoc)
+{
+  uint32_t count = 0;
+  aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
+  return count;
+}
+
+bool
+nsDocument::IsFullscreenLeaf()
+{
+  // A fullscreen leaf document is fullscreen, and has no fullscreen
+  // subdocuments.
+  if (!IsFullScreenDoc()) {
+    return false;
+  }
+  return CountFullscreenSubDocuments(this) == 0;
+}
+
+static bool
+ResetFullScreen(nsIDocument* aDocument, void* aData)
+{
+  if (aDocument->IsFullScreenDoc()) {
+    NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
+        "Should have at most 1 fullscreen subdocument.");
+    static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
+    NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
+    nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
+    changed->AppendElement(aDocument);
+
+    if (HasCrossProcessParent(aDocument)) {
+      // We're at the top of the content-process side doc tree. Ask the parent
+      // process to exit fullscreen.
+      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+      os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
+    }
+
+    // Dispatch a notification so that if this document has any
+    // cross-process subdocuments, they'll be notified to exit fullscreen.
+    // The BrowserElementParent listens for this event and performs the
+    // cross process notification if it has a remote child process.
+    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
+
+    aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
+  }
+  return true;
+}
+
+static void
+ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
+{
+  MOZ_ASSERT(aMaybeNotARootDoc);
+  nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
+  NS_ASSERTION(root, "Should have root when in fullscreen!");
   if (!root) {
-    // Not in full-screen mode.
     return;
   }
   NS_ASSERTION(root->IsFullScreenDoc(),
-    "Full-screen root should be a full-screen doc...");
+    "Fullscreen root should be a fullscreen doc...");
 
   // Stores a list of documents to which we must dispatch "mozfullscreenchange".
   // We're required by the spec to dispatch the events in leaf-to-root
-  // order when exiting full-screen, but we traverse the doctree in a
+  // order when exiting fullscreen, but we traverse the doctree in a
   // root-to-leaf order, so we save references to the documents we must
   // dispatch to so that we dispatch in the specified order.
   nsAutoTArray<nsIDocument*, 8> changed;
 
-  // We may also need to unlock the pointer, if it's locked.
-  nsCOMPtr<Element> pointerLockedElement =
-    do_QueryReferent(nsEventStateManager::sPointerLockedElement);
-  if (pointerLockedElement) {
-    UnlockPointer();
-  }
-
-  // Walk the tree of full-screen documents, and reset their full-screen state.
+  // Walk the tree of fullscreen documents, and reset their fullscreen state.
   ResetFullScreen(root, static_cast<void*>(&changed));
 
   // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
   // order so that the events for the leaf document arrives before the root
   // document, as required by the spec.
   for (uint32_t i = 0; i < changed.Length(); ++i) {
     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
   }
 
-  // Reset global state. Do this before we move the window out of full-screen
-  // mode, as that calls nsGlobalWindow::SetFullScreen() which calls back into
-  // nsIDocument::ExitFullScreen().
-  sFullScreenRootDoc = nullptr;
-  sFullScreenDoc = nullptr;
-
-  // Move the top-level window out of full-screen mode.
+  NS_ASSERTION(!root->IsFullScreenDoc(),
+    "Fullscreen root should no longer be a fullscreen doc...");
+
+  // Move the top-level window out of fullscreen mode.
   SetWindowFullScreen(root, false);
 }
 
+/* static */
+void
+nsDocument::ExitFullscreen(nsIDocument* aDoc)
+{
+  // Unlock the pointer, if it's locked.
+  nsCOMPtr<Element> pointerLockedElement =
+    do_QueryReferent(nsEventStateManager::sPointerLockedElement);
+  if (pointerLockedElement) {
+    UnlockPointer();
+  }
+
+  if (aDoc)  {
+    ExitFullscreenInDocTree(aDoc);
+    return;
+  }
+
+  // Clear fullscreen stacks in all fullscreen roots' descendant documents.
+  FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
+  NS_ASSERTION(FullscreenRoots::IsEmpty(),
+      "Should have exited all fullscreen roots from fullscreen");
+}
+
+bool
+GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
+{
+  if (aDoc->IsFullscreenLeaf()) {
+    nsIDocument** result = static_cast<nsIDocument**>(aData);
+    *result = aDoc;
+    return false;
+  } else if (aDoc->IsFullScreenDoc()) {
+    aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
+  }
+  return true;
+}
+
+static nsIDocument*
+GetFullscreenLeaf(nsIDocument* aDoc)
+{
+  nsIDocument* leaf = nullptr;
+  GetFullscreenLeaf(aDoc, &leaf);
+  if (leaf) {
+    return leaf;
+  }
+  // Otherwise we could be either in a non-fullscreen doc tree, or we're
+  // below the fullscreen doc. Start the search from the root.
+  nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
+  // Check that the root is actually fullscreen so we don't waste time walking
+  // around its descendants.
+  if (!root->IsFullScreenDoc()) {
+    return nullptr;
+  }
+  GetFullscreenLeaf(root, &leaf);
+  return leaf;
+}
+
 void
 nsDocument::RestorePreviousFullScreenState()
 {
-  NS_ASSERTION(!IsFullScreenDoc() || sFullScreenDoc != nullptr,
-               "Should have a full-screen doc when full-screen!");
-
-  if (!IsFullScreenDoc() || !GetWindow() || !sFullScreenDoc) {
+  NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
+    "Should have at least 1 fullscreen root when fullscreen!");
+
+  if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
     return;
   }
 
   // If fullscreen mode is updated the pointer should be unlocked
   nsCOMPtr<Element> pointerLockedElement =
     do_QueryReferent(nsEventStateManager::sPointerLockedElement);
   if (pointerLockedElement) {
     UnlockPointer();
   }
 
-  nsCOMPtr<nsIDocument> fullScreenDoc(do_QueryReferent(sFullScreenDoc));
+  nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
 
   // The fullscreen document may contain a <iframe mozbrowser> element which
   // has a cross process child. So send a notification so that its browser
   // parent will send a message to its child process to also exit fullscreen.
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr);
 
   // Clear full-screen stacks in all descendant in process documents, bottom up.
@@ -9284,28 +9524,25 @@ nsDocument::RestorePreviousFullScreenSta
         // as necessary.
         nsAutoString origin;
         nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin);
         nsIDocument* root = nsContentUtils::GetRootDocument(doc);
         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
         os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
       }
 
-      sFullScreenDoc = do_GetWeakReference(doc);
       break;
     }
   }
 
   if (doc == nullptr) {
-    // We moved all documents out of full-screen mode, reset global full-screen
-    // state and move the top-level window out of full-screen mode.
-    DebugOnly< nsCOMPtr<nsIDocument> > root(do_QueryReferent(sFullScreenRootDoc));
-    NS_ASSERTION(!root->IsFullScreenDoc(), "Should have cleared all docs' stacks");
-    sFullScreenDoc = nullptr;
-    sFullScreenRootDoc = nullptr;
+    // We moved all documents in this doctree out of fullscreen mode,
+    // move the top-level window out of fullscreen mode.
+    NS_ASSERTION(!nsContentUtils::GetRootDocument(this)->IsFullScreenDoc(),
+      "Should have cleared all docs' stacks");
     SetWindowFullScreen(this, false);
   }
 }
 
 bool
 nsDocument::IsFullScreenDoc()
 {
   return GetFullScreenElement() != nullptr;
@@ -9364,34 +9601,43 @@ LogFullScreenDenied(bool aLogFailure, co
                                   "DOM", aDoc,
                                   nsContentUtils::eDOM_PROPERTIES,
                                   aMessage);
 }
 
 nsresult
 nsDocument::AddFullscreenApprovedObserver()
 {
+  NS_ASSERTION(!mHasFullscreenApprovedObserver, "Don't add observer twice.");
+
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
 
   nsresult res = os->AddObserver(this, "fullscreen-approved", true);
   NS_ENSURE_SUCCESS(res, res);
 
+  mHasFullscreenApprovedObserver = true;
+
   return NS_OK;
 }
 
 nsresult
 nsDocument::RemoveFullscreenApprovedObserver()
 {
+  if (!mHasFullscreenApprovedObserver) {
+    return NS_OK;
+  }
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
 
   nsresult res = os->RemoveObserver(this, "fullscreen-approved");
   NS_ENSURE_SUCCESS(res, res);
 
+  mHasFullscreenApprovedObserver = false;
+
   return NS_OK;
 }
 
 void
 nsDocument::CleanupFullscreenState()
 {
   if (!mFullScreenStack.IsEmpty()) {
     // The top element in the full-screen stack will have full-screen
@@ -9401,16 +9647,17 @@ nsDocument::CleanupFullscreenState()
     NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
     if (top) {
       nsEventStateManager::SetFullScreenState(top, false);
     }
     mFullScreenStack.Clear();
   }
   SetApprovedForFullscreen(false);
   RemoveFullscreenApprovedObserver();
+  mFullscreenRoot = nullptr;
 }
 
 bool
 nsDocument::FullScreenStackPush(Element* aElement)
 {
   NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
   Element* top = FullScreenStackTop();
   if (top == aElement || !aElement) {
@@ -9579,17 +9826,17 @@ nsDocument::RequestFullScreen(Element* a
   }
   if (!IsFullScreenEnabled(aWasCallerChrome, true)) {
     // IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
     return;
   }
   if (GetFullScreenElement() &&
       !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
     // If this document is full-screen, only grant full-screen requests from
-    // a descendent of the current full-screen element.
+    // a descendant of the current full-screen element.
     LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
     return;
   }
   if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
     LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this);
     return;
   }
   // Deny requests when a windowed plugin is focused.
@@ -9603,33 +9850,35 @@ nsDocument::RequestFullScreen(Element* a
   if (focusedElement) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
     if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
       LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this);
       return;
     }
   }
 
+  // Stash a reference to any existing fullscreen doc, we'll use this later
+  // to detect if the origin which is fullscreen has changed.
+  nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
+
   AddFullscreenApprovedObserver();
 
   // Stores a list of documents which we must dispatch "mozfullscreenchange"
   // too. We're required by the spec to dispatch the events in root-to-leaf
   // order, but we traverse the doctree in a leaf-to-root order, so we save
   // references to the documents we must dispatch to so that we get the order
   // as specified.
   nsAutoTArray<nsIDocument*, 8> changed;
 
   // Remember the root document, so that if a full-screen document is hidden
   // we can reset full-screen state in the remaining visible full-screen documents.
   nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
-  sFullScreenRootDoc = do_GetWeakReference(fullScreenRootDoc);
-
-  // If a document is already in fullscreen, then unlock the mouse pointer
-  // before setting a new document to fullscreen
-  if (sFullScreenDoc) {
+  if (fullScreenRootDoc->IsFullScreenDoc()) {
+    // A document is already in fullscreen, unlock the mouse pointer
+    // before setting a new document to fullscreen
     UnlockPointer();
   }
 
   // If a document is already in fullscreen, then unlock the mouse pointer
   // before setting a new document to fullscreen
   nsCOMPtr<Element> pointerLockedElement =
     do_QueryReferent(nsEventStateManager::sPointerLockedElement);
   if (pointerLockedElement) {
@@ -9645,16 +9894,19 @@ nsDocument::RequestFullScreen(Element* a
 
   // Propagate up the document hierarchy, setting the full-screen element as
   // the element's container in ancestor documents. This also sets the
   // appropriate css styles as well. Note we don't propagate down the
   // document hierarchy, the full-screen element (or its container) is not
   // visible there. Stop when we reach the root document.
   nsIDocument* child = this;
   while (true) {
+    child->SetFullscreenRoot(fullScreenRootDoc);
+    NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
+        "Fullscreen root should be set!");
     nsIDocument* parent = child->GetParentDocument();
     if (!parent) {
       break;
     }
     Element* element = parent->FindContentForSubDocument(child)->AsElement();
     if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
       changed.AppendElement(parent);
       child = parent;
@@ -9684,34 +9936,29 @@ nsDocument::RequestFullScreen(Element* a
       !Preferences::GetBool("full-screen-api.approval-required") ||
       NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
       nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen");
   }
 
   // If this document, or a document with the same principal has not
   // already been approved for fullscreen this fullscreen-session, dispatch
   // an event so that chrome knows to pop up a warning/approval UI.
-  nsCOMPtr<nsIDocument> previousFullscreenDoc(do_QueryReferent(sFullScreenDoc));
   // Note previousFullscreenDoc=nullptr upon first entry, so we always
   // take this path on the first time we enter fullscreen in a fullscreen
   // session.
   if (!mIsApprovedForFullscreen ||
       !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
     nsRefPtr<nsAsyncDOMEvent> e =
       new nsAsyncDOMEvent(this,
                           NS_LITERAL_STRING("MozEnteredDomFullscreen"),
                           true,
                           true);
     e->PostDOMEvent();
   }
 
-  // Remember this is the requesting full-screen document.
-  sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
-  NS_ASSERTION(sFullScreenDoc, "nsDocument should support weak ref!");
-
 #ifdef DEBUG
   // Note assertions must run before SetWindowFullScreen() as that does
   // synchronous event dispatch which can run script which exits full-screen!
   NS_ASSERTION(GetFullScreenElement() == aElement,
                "Full-screen element should be the requested element!");
   NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
   nsCOMPtr<nsIDOMElement> fse;
   GetMozFullScreenElement(getter_AddRefs(fse));
@@ -9798,33 +10045,21 @@ nsDocument::GetMozFullScreenEnabled(bool
 
 bool
 nsDocument::MozFullScreenEnabled()
 {
   return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false);
 }
 
 static bool
-HasFullScreenSubDocument(nsIDocument* aDoc, void* aData)
-{
-  if (aDoc->IsFullScreenDoc()) {
-    // This subdocument is full-screen. Set result and return false to
-    // stop iteration.
-    *static_cast<bool*>(aData) = true;
-    return false;
-  }
-  return true;
-}
-
-static bool
 HasFullScreenSubDocument(nsIDocument* aDoc)
 {
-  bool result = false;
-  aDoc->EnumerateSubDocuments(&HasFullScreenSubDocument, static_cast<void*>(&result));
-  return result;
+  uint32_t count = CountFullscreenSubDocuments(aDoc);
+  NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
+  return count >= 1;
 }
 
 bool
 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
 {
   if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
     // Chrome code can always use the full-screen API, provided it's not
     // explicitly disabled. Note IsCallerChrome() returns true when running
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -938,24 +938,27 @@ public:
   bool HasAudioAvailableListeners()
   {
     return mHasAudioAvailableListener;
   }
 
   virtual Element* GetFullScreenElement();
   virtual void AsyncRequestFullScreen(Element* aElement);
   virtual void RestorePreviousFullScreenState();
+  virtual bool IsFullscreenLeaf();
   virtual bool IsFullScreenDoc();
   virtual void SetApprovedForFullscreen(bool aIsApproved);
   virtual nsresult RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
                                                 const nsAString& aNewOrigin);
 
   virtual nsresult RemoteFrameFullscreenReverted();
+  virtual nsIDocument* GetFullscreenRoot();
+  virtual void SetFullscreenRoot(nsIDocument* aRoot);
 
-  static void ExitFullScreen();
+  static void ExitFullscreen(nsIDocument* aDoc);
 
   // This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
   // to move this document into full-screen mode if allowed. aWasCallerChrome
   // should be true when nsIDocument::AsyncRequestFullScreen() was called
   // by chrome code. aNotifyOnOriginChange denotes whether we should send a
   // fullscreen-origin-change notification if requesting fullscreen in this
   // document causes the origin which is fullscreen to change. We may want to
   // *not* send this notification if we're calling RequestFullscreen() as part
@@ -1158,41 +1161,34 @@ protected:
   // the script global object of the original document.
   nsWeakPtr mScriptObject;
 
   // Weak reference to the scope object (aka the script global object)
   // that, unlike mScriptGlobalObject, is never unset once set. This
   // is a weak reference to avoid leaks due to circular references.
   nsWeakPtr mScopeObject;
 
-  // The document which requested (and was granted) full-screen. All ancestors
-  // of this document will also be full-screen.
-  static nsWeakPtr sFullScreenDoc;
-
-  // The root document of the doctree containing the document which requested
-  // full-screen. This root document will also be in full-screen state, as will
-  // all the descendents down to the document which requested full-screen. This
-  // reference allows us to reset full-screen state on all documents when a
-  // document is hidden/navigation occurs.
-  static nsWeakPtr sFullScreenRootDoc;
-
   // Weak reference to the document which owned the pending pointer lock
   // element, at the time it requested pointer lock.
   static nsWeakPtr sPendingPointerLockDoc;
 
   // Weak reference to the element which requested pointer lock. This request
   // is "pending", and will be processed once the element's document has had
   // the "fullscreen" permission granted.
   static nsWeakPtr sPendingPointerLockElement;
 
   // Stack of full-screen elements. When we request full-screen we push the
   // full-screen element onto this stack, and when we cancel full-screen we
   // pop one off this stack, restoring the previous full-screen state
   nsTArray<nsWeakPtr> mFullScreenStack;
 
+  // The root of the doc tree in which this document is in. This is only
+  // non-null when this document is in fullscreen mode.
+  nsWeakPtr mFullscreenRoot;
+
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
@@ -1252,16 +1248,21 @@ protected:
   // flag.
   //
   // Note we must track this separately from the "fullscreen" permission,
   // so that pending pointer lock requests can determine whether documents
   // whose principal doesn't have a host (i.e. those which can't store
   // permissions in the permission manager) have been approved for fullscreen.
   bool mIsApprovedForFullscreen:1;
 
+  // Whether this document has a fullscreen approved observer. Only documents
+  // which request fullscreen and which don't have a pre-existing approval for
+  // fullscreen will have an observer.
+  bool mHasFullscreenApprovedObserver:1;
+
   uint8_t mXMLDeclarationBits;
 
   nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject> *mBoxObjectTable;
 
   // The channel that got passed to StartDocumentLoad(), if any
   nsCOMPtr<nsIChannel> mChannel;
   nsRefPtr<nsHTMLCSSStyleSheet> mStyleAttrStyleSheet;
 
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -81,17 +81,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
 #include "sampler.h"
 
 #include "jsapi.h"
-#include "nsHTMLIFrameElement.h"
+#include "mozilla/dom/HTMLIFrameElement.h"
 #include "nsSandboxFlags.h"
 
 #include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
@@ -445,18 +445,18 @@ nsFrameLoader::ReallyStartLoadingInterna
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
   mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
   NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
 
   // Is this an <iframe> with a sandbox attribute or a parent which is
   // sandboxed ?
-  nsHTMLIFrameElement* iframe =
-    nsHTMLIFrameElement::FromContent(mOwnerContent);
+  HTMLIFrameElement* iframe =
+    HTMLIFrameElement::FromContent(mOwnerContent);
 
   uint32_t sandboxFlags = 0;
 
   if (iframe) {
     sandboxFlags = iframe->GetSandboxFlags();
 
     uint32_t parentSandboxFlags = iframe->OwnerDoc()->GetSandboxFlags();
 
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -81,17 +81,18 @@ nsImageLoadingContent::nsImageLoadingCon
     mLoading(false),
     // mBroken starts out true, since an image without a URI is broken....
     mBroken(true),
     mUserDisabled(false),
     mSuppressed(false),
     mNewRequestsWillNeedAnimationReset(false),
     mStateChangerDepth(0),
     mCurrentRequestRegistered(false),
-    mPendingRequestRegistered(false)
+    mPendingRequestRegistered(false),
+    mVisibleCount(0)
 {
   if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) {
     mLoadingEnabled = false;
   }
 }
 
 void
 nsImageLoadingContent::DestroyImageLoadingContent()
@@ -116,16 +117,21 @@ NS_IMETHODIMP
 nsImageLoadingContent::Notify(imgIRequest* aRequest,
                               int32_t aType,
                               const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
+  if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
+    OnUnlockedDraw();
+    return NS_OK;
+  }
+
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     // We should definitely have a request here
     NS_ABORT_IF_FALSE(aRequest, "no request?");
 
     NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
                     "Unknown request");
   }
 
@@ -223,16 +229,35 @@ nsImageLoadingContent::OnStopRequest(img
   }
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
+void
+nsImageLoadingContent::OnUnlockedDraw()
+{
+  if (mVisibleCount > 0) {
+    // We should already be marked as visible, there is nothing more we can do.
+    return;
+  }
+
+  nsPresContext* presContext = GetFramePresContext();
+  if (!presContext)
+    return;
+
+  nsIPresShell* presShell = presContext->PresShell();
+  if (!presShell)
+    return;
+
+  presShell->EnsureImageInVisibleList(this);
+}
+
 nsresult
 nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
 {
   bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
   if (requestFlag) {
     nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
                                         aRequest, requestFlag);
   }
@@ -370,35 +395,33 @@ nsImageLoadingContent::GetRequest(int32_
   return result.ErrorCode();
 }
 
 NS_IMETHODIMP_(void)
 nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
 {
   NS_ASSERTION(aFrame, "aFrame is null");
 
+  if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
+    // Assume all images in popups are visible.
+    IncrementVisibleCount();
+  }
+
+  nsPresContext* presContext = aFrame->PresContext();
+  if (mVisibleCount == 0) {
+    presContext->PresShell()->EnsureImageInVisibleList(this);
+  }
+
+  // We pass the SKIP_FRAME_CHECK flag to TrackImage here because our primary
+  // frame pointer hasn't been setup yet when this is caled.
+  TrackImage(mCurrentRequest, SKIP_FRAME_CHECK);
+  TrackImage(mPendingRequest, SKIP_FRAME_CHECK);
+
   // We need to make sure that our image request is registered, if it should
   // be registered.
-  nsPresContext* presContext = aFrame->PresContext();
-
-  if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
-    nsIDocument* doc = GetOurCurrentDoc();
-    if (doc) {
-      mCurrentRequestFlags |= REQUEST_IS_TRACKED;
-      doc->AddImage(mCurrentRequest);
-    }
-  }
-  if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
-    nsIDocument* doc = GetOurCurrentDoc();
-    if (doc) {
-      mPendingRequestFlags |= REQUEST_IS_TRACKED;
-      doc->AddImage(mPendingRequest);
-    }
-  }
-
   if (mCurrentRequest) {
     nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
                                                   &mCurrentRequestRegistered);
   }
 
   if (mPendingRequest) {
     nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest,
                                                   &mPendingRequestRegistered);
@@ -418,29 +441,23 @@ nsImageLoadingContent::FrameDestroyed(ns
   }
 
   if (mPendingRequest) {
     nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
                                           mPendingRequest,
                                           &mPendingRequestRegistered);
   }
 
-  if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
-    nsIDocument* doc = GetOurCurrentDoc();
-    if (doc) {
-      mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
-      doc->RemoveImage(mCurrentRequest);
-    }
-  }
-  if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
-    nsIDocument* doc = GetOurCurrentDoc();
-    if (doc) {
-      mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
-      doc->RemoveImage(mPendingRequest);
-    }
+  UntrackImage(mCurrentRequest);
+  UntrackImage(mPendingRequest);
+
+  if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
+    // We assume all images in popups are visible, so this decrement balances
+    // out the increment in FrameCreated above.
+    DecrementVisibleCount();
   }
 }
 
 int32_t
 nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
                                       ErrorResult& aError)
 {
   if (aRequest == mCurrentRequest) {
@@ -609,16 +626,44 @@ nsImageLoadingContent::UnblockOnload(img
   nsIDocument* doc = GetOurCurrentDoc();
   if (doc) {
     doc->UnblockOnload(false);
   }
 
   return NS_OK;
 }
 
+void
+nsImageLoadingContent::IncrementVisibleCount()
+{
+  mVisibleCount++;
+  if (mVisibleCount == 1) {
+    TrackImage(mCurrentRequest);
+    TrackImage(mPendingRequest);
+  }
+}
+
+void
+nsImageLoadingContent::DecrementVisibleCount()
+{
+  NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here");
+  mVisibleCount--;
+
+  if (mVisibleCount == 0) {
+    UntrackImage(mCurrentRequest);
+    UntrackImage(mPendingRequest);
+  }
+}
+
+uint32_t
+nsImageLoadingContent::GetVisibleCount()
+{
+  return mVisibleCount;
+}
+
 /*
  * Non-interface methods
  */
 
 nsresult
 nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
                                  bool aForce,
                                  bool aNotify)
@@ -1096,17 +1141,17 @@ nsImageLoadingContent::ClearCurrentReque
                     "Shouldn't have both mCurrentRequest and mCurrentURI!");
 
   // Deregister this image from the refresh driver so it no longer receives
   // notifications.
   nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
                                         &mCurrentRequestRegistered);
 
   // Clean up the request.
-  UntrackImage(mCurrentRequest);
+  UntrackImage(mCurrentRequest, REQUEST_DISCARD);
   mCurrentRequest->CancelAndForgetObserver(aReason);
   mCurrentRequest = nullptr;
   mCurrentRequestFlags = 0;
 }
 
 void
 nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
 {
@@ -1119,17 +1164,17 @@ nsImageLoadingContent::ClearPendingReque
   nsCxPusher pusher;
   pusher.PushNull();
 
   // Deregister this image from the refresh driver so it no longer receives
   // notifications.
   nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
                                         &mPendingRequestRegistered);
 
-  UntrackImage(mPendingRequest);
+  UntrackImage(mPendingRequest, REQUEST_DISCARD);
   mPendingRequest->CancelAndForgetObserver(aReason);
   mPendingRequest = nullptr;
   mPendingRequestFlags = 0;
 }
 
 bool*
 nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
 {
@@ -1178,26 +1223,18 @@ nsImageLoadingContent::BindToTree(nsIDoc
   if (!aDocument)
     return;
 
   // Push a null JSContext on the stack so that callbacks triggered by the
   // below code won't think they're being called from JS.
   nsCxPusher pusher;
   pusher.PushNull();
 
-  if (GetOurPrimaryFrame()) {
-    if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
-      mCurrentRequestFlags |= REQUEST_IS_TRACKED;
-      aDocument->AddImage(mCurrentRequest);
-    }
-    if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
-      mPendingRequestFlags |= REQUEST_IS_TRACKED;
-      aDocument->AddImage(mPendingRequest);
-    }
-  }
+  TrackImage(mCurrentRequest);
+  TrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     aDocument->BlockOnload();
 }
 
 void
 nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
 {
@@ -1206,73 +1243,70 @@ nsImageLoadingContent::UnbindFromTree(bo
   if (!doc)
     return;
 
   // Push a null JSContext on the stack so that callbacks triggered by the
   // below code won't think they're being called from JS.
   nsCxPusher pusher;
   pusher.PushNull();
 
-  if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
-    mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
-    doc->RemoveImage(mCurrentRequest);
-  }
-  if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
-    mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
-    doc->RemoveImage(mPendingRequest);
-  }
+  UntrackImage(mCurrentRequest);
+  UntrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     doc->UnblockOnload(false);
 }
 
 nsresult
-nsImageLoadingContent::TrackImage(imgIRequest* aImage)
+nsImageLoadingContent::TrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */)
 {
   if (!aImage)
     return NS_OK;
 
   MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
              "Why haven't we heard of this request?");
 
   nsIDocument* doc = GetOurCurrentDoc();
-  if (doc && GetOurPrimaryFrame()) {
+  if (doc && ((aFlags & SKIP_FRAME_CHECK) || GetOurPrimaryFrame()) &&
+      (mVisibleCount > 0)) {
     if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
       mCurrentRequestFlags |= REQUEST_IS_TRACKED;
       doc->AddImage(mCurrentRequest);
     }
     if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
       mPendingRequestFlags |= REQUEST_IS_TRACKED;
       doc->AddImage(mPendingRequest);
     }
   }
   return NS_OK;
 }
 
 nsresult
-nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
+nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */)
 {
   if (!aImage)
     return NS_OK;
 
   MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
              "Why haven't we heard of this request?");
 
   // If GetOurDocument() returns null here, we've outlived our document.
   // That's fine, because the document empties out the tracker and unlocks
   // all locked images on destruction.
   nsIDocument* doc = GetOurCurrentDoc();
   if (doc) {
     if (aImage == mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
       mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
-      doc->RemoveImage(mCurrentRequest, nsIDocument::REQUEST_DISCARD);
+      doc->RemoveImage(mCurrentRequest,
+                       (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
     }
     if (aImage == mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
       mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
-      doc->RemoveImage(mPendingRequest, nsIDocument::REQUEST_DISCARD);
+      doc->RemoveImage(mPendingRequest,
+                       (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
     }
   }
   return NS_OK;
 }
 
 
 void
 nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -178,16 +178,17 @@ protected:
   virtual mozilla::CORSMode GetCORSMode();
 
   // Subclasses are *required* to call BindToTree/UnbindFromTree.
   void BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                   nsIContent* aBindingParent, bool aCompileEventHandlers);
   void UnbindFromTree(bool aDeep, bool aNullParent);
 
   nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus);
+  void OnUnlockedDraw();
   nsresult OnImageIsAnimated(imgIRequest *aRequest);
 
 private:
   /**
    * Struct used to manage the image observers.
    */
   struct ImageObserver {
     ImageObserver(imgINotificationObserver* aObserver);
@@ -321,19 +322,31 @@ protected:
    * image may be null.
    */
   static bool HaveSize(imgIRequest *aImage);
 
   /**
    * Adds/Removes a given imgIRequest from our document's tracker.
    *
    * No-op if aImage is null.
+   *
+   * SKIP_FRAME_CHECK passed to TrackImage means we skip the check if we have a
+   * frame, there is only one valid use of this: when calling from FrameCreated.
+   *
+   * REQUEST_DISCARD passed to UntrackImage means we request the discard of the
+   * decoded data of the image.
    */
-  nsresult TrackImage(imgIRequest* aImage);
-  nsresult UntrackImage(imgIRequest* aImage);
+  enum {
+    SKIP_FRAME_CHECK = 0x1
+  };
+  nsresult TrackImage(imgIRequest* aImage, uint32_t aFlags = 0);
+  enum {
+    REQUEST_DISCARD = 0x1
+  };
+  nsresult UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0);
 
   /* MEMBERS */
   nsRefPtr<imgRequestProxy> mCurrentRequest;
   nsRefPtr<imgRequestProxy> mPendingRequest;
   uint32_t mCurrentRequestFlags;
   uint32_t mPendingRequestFlags;
 
   enum {
@@ -399,11 +412,13 @@ protected:
 private:
   /* The number of nested AutoStateChangers currently tracking our state. */
   uint8_t mStateChangerDepth;
 
   // Flags to indicate whether each of the current and pending requests are
   // registered with the refresh driver.
   bool mCurrentRequestRegistered;
   bool mPendingRequestRegistered;
+
+  uint32_t mVisibleCount;
 };
 
 #endif // nsImageLoadingContent_h__
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -1020,17 +1020,18 @@ nsScriptLoader::ConvertToUTF16(nsIChanne
     // the BOM for endianness. Both the UTF-16 and the UTF-8 decoder will
     // take care of swallowing the BOM.
     charsetConv->GetUnicodeDecoderRaw(charset.get(),
                                       getter_AddRefs(unicodeDecoder));
   }
 
   if (!unicodeDecoder &&
       aChannel &&
-      NS_SUCCEEDED(aChannel->GetContentCharset(charset))) {
+      NS_SUCCEEDED(aChannel->GetContentCharset(charset)) &&
+      !charset.IsEmpty()) {
     charsetConv->GetUnicodeDecoder(charset.get(),
                                    getter_AddRefs(unicodeDecoder));
   }
 
   if (!unicodeDecoder && !aHintCharset.IsEmpty()) {
     CopyUTF16toUTF8(aHintCharset, charset);
     charsetConv->GetUnicodeDecoder(charset.get(),
                                    getter_AddRefs(unicodeDecoder));
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3366,16 +3366,18 @@ void
 nsXMLHttpRequest::SetMultipart(bool aMultipart, nsresult& aRv)
 {
   if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
     // Can't change this while we're in the middle of something.
     aRv = NS_ERROR_IN_PROGRESS;
     return;
   }
 
+  LogMessage("MultipartXHRWarning", GetOwner());
+
   if (aMultipart) {
     mState |= XML_HTTP_REQUEST_MULTIPART;
   } else {
     mState &= ~XML_HTTP_REQUEST_MULTIPART;
   }
 }
 
 /* attribute boolean mozBackgroundRequest; */
--- a/content/base/test/test_XHR_anon.html
+++ b/content/base/test/test_XHR_anon.html
@@ -19,20 +19,18 @@
 // An XHR with the anon flag set will not send cookie and auth information.
 const TEST_URL = "http://example.com/tests/content/base/test/file_XHR_anon.sjs";
 document.cookie = "foo=bar";
 
 let am = {
   authMgr: null,
 
   init: function() {
-    const {classes: Cc, interfaces: Ci} = SpecialPowers.wrap(Components);
-
-    this.authMgr = Cc["@mozilla.org/network/http-auth-manager;1"]
-                     .getService(Components.interfaces.nsIHttpAuthManager)
+    this.authMgr = SpecialPowers.Cc["@mozilla.org/network/http-auth-manager;1"]
+                                .getService(SpecialPowers.Ci.nsIHttpAuthManager)
   },
 
   addIdentity: function() {
     this.authMgr.setAuthIdentity("http", "example.com", -1, "basic", "testrealm",
                                  "", "example.com", "user1", "password1");
   },
 
   tearDown: function() {
--- a/content/base/test/test_bug425013.html
+++ b/content/base/test/test_bug425013.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body onload="runtests();">
 
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=425013">Mozilla Bug 425013</a>
 <br>
 
 <script type="text/javascript;version=1.7">
 var missingPlugins = new Array();
-var OBJLC = Components.interfaces.nsIObjectLoadingContent;
+var OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
 
 function pluginBinding(event)
 {
   var plugin = event.target;
   plugin instanceof OBJLC;
   if (SpecialPowers.wrap(plugin).pluginFallbackType == OBJLC.PLUGIN_UNSUPPORTED)
     missingPlugins.push(plugin);
 }
--- a/content/base/test/test_bug429157.html
+++ b/content/base/test/test_bug429157.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body onload="runtests();">
 
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=429157">Mozilla Bug 429157</a>
 <br>
 
 <script>
 var missingPlugins = new Array();
-const OBJLC = Components.interfaces.nsIObjectLoadingContent;
+const OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
 
 function pluginBindingAttached(event)
 {
   var plugin = event.target;
   plugin instanceof OBJLC;
   if (SpecialPowers.wrap(plugin).pluginFallbackType == OBJLC.PLUGIN_UNSUPPORTED)
     missingPlugins.push(plugin);
 }
--- a/content/base/test/test_bug527896.html
+++ b/content/base/test/test_bug527896.html
@@ -17,16 +17,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 527896 **/
 
 SimpleTest.waitForExplicitFinish();
 
+SimpleTest.expectAssertions(1);
+
 var docWrittenSrcExecuted = false;
 var scriptInsertedSrcExecuted = false;
 
 // the iframe test runs with the HTML5 parser
 
 var iframe = document.getElementsByTagName('iframe')[0];
 iframe.contentWindow.document.open();
 iframe.contentWindow.document.write("<!DOCTYPE html>");
--- a/content/base/test/test_bug548193.html
+++ b/content/base/test/test_bug548193.html
@@ -8,16 +8,18 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <iframe style="width:200px;height:200px;" id='cspframe'></iframe>
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(1);
+
 // This is used to watch requests go out so we can see if the report is
 // sent correctly
 function examiner() {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   var obsvc = Components.classes['@mozilla.org/observer-service;1']
                         .getService(Components.interfaces.nsIObserverService);
   obsvc.addObserver(this, "http-on-modify-request", false);
 }
--- a/content/base/test/test_bug638112.html
+++ b/content/base/test/test_bug638112.html
@@ -11,16 +11,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=638112">Mozilla Bug 638112</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="text/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 638112 **/
 
 function run_test() {
   var req = new XMLHttpRequest();
   req.open("GET", "bug638112.sjs", false);
   req.send(null);
   var statusText = req.statusText;
 
--- a/content/base/test/test_bug810494.html
+++ b/content/base/test/test_bug810494.html
@@ -14,17 +14,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=810494">Mozilla Bug 810494</a>
 <pre id="test">
 <script type="application/javascript;version=1.8">
 
 function test(tag, type) {
   "use strict";
   info("testing " + tag + " tag with type " + type);
 
-  const OBJLC = Components.interfaces.nsIObjectLoadingContent;
+  const OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
   let obj = document.createElement(tag);
   obj.type = type;
   document.body.appendChild(obj);
 
   obj instanceof OBJLC;
   obj = SpecialPowers.wrap(obj);
 
   // We expect this tag to simply go to alternate content, not get a
--- a/content/base/test/test_child_process_shutdown_message.html
+++ b/content/base/test/test_child_process_shutdown_message.html
@@ -11,24 +11,22 @@
 <p id="display">
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = SpecialPowers.wrap(Components);
-
 const APP_URL = "http://example.org";
 const APP_MANIFEST = "http://example.org/manifest.webapp";
 const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
 
-let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-             .getService(Ci.nsIMessageBroadcaster);
+let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
+                        .getService(SpecialPowers.Ci.nsIMessageBroadcaster);
 
 /**
  * Load the example.org site in an <iframe mozbrowser>
  *
  * @param isApp
  *        If true, the example.org site will be loaded as an app.
  */
 function loadBrowser(isApp, callback) {
--- a/content/base/test/test_messagemanager_assertpermission.html
+++ b/content/base/test/test_messagemanager_assertpermission.html
@@ -10,28 +10,26 @@
 <p id="display">
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = SpecialPowers.wrap(Components);
-
 const APP_URL = "http://example.org";
 const APP_MANIFEST = "http://example.org/manifest.webapp";
 const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
 
-let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-             .getService(Ci.nsIMessageBroadcaster);
-let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
-             .getService(Ci.nsISyncMessageSender);
-let gAppsService = Cc["@mozilla.org/AppsService;1"]
-                     .getService(Ci.nsIAppsService);
+let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
+                        .getService(SpecialPowers.Ci.nsIMessageBroadcaster);
+let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
+                        .getService(SpecialPowers.Ci.nsISyncMessageSender);
+let gAppsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"]
+                     .getService(SpecialPowers.Ci.nsIAppsService);
 
 function setUp() {
   SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
   SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", true);
   SpecialPowers.addPermission("browser", true, window.document);
   SpecialPowers.addPermission("embed-apps", true, window.document);
 
   let appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST);
--- a/content/base/test/test_mutationobservers.html
+++ b/content/base/test/test_mutationobservers.html
@@ -13,16 +13,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641821">Mozilla Bug 641821</a>
 <p id="display"></p>
 <div id="content" style="display: none">
                                 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") != 0) {
+  // for non-Windows
+  SimpleTest.expectAssertions(1);
+}
+
 /** Test for Bug 641821 **/
 
 var div = document.createElement("div");
 
 var M;
 if ("MozMutationObserver" in window) {
   M = window.MozMutationObserver;
 } else if ("WebKitMutationObserver" in window) {
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -40,18 +40,18 @@ var OPTIONS = {
       throw error;
     }
     return request.responseText;
   };
 
 SimpleTest.waitForExplicitFinish();
 
 function detectDriverType() {
-  const Cc = SpecialPowers.wrap(Components).classes;
-  const Ci = SpecialPowers.wrap(Components).interfaces;
+  const Cc = SpecialPowers.Cc;
+  const Ci = SpecialPowers.Ci;
   var doc = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser).parseFromString("<html/>", "text/html");
 
   var canvas = doc.createElement("canvas");
   canvas.width = 1;
   canvas.height = 1;
 
   var type = "";
   var gl;
@@ -100,25 +100,25 @@ function start() {
   // Set kMacVersion to the OS X version for Mac, and 0 otherwise.
   var osxmatch = /Mac OS X (\d+.\d+)/.exec(navigator.userAgent);
   var kMacVersion = osxmatch ? parseFloat(osxmatch[1]) : 0;
 
   var kIsWindowsVistaOrHigher = false;
   if (kIsWindows) {
     // code borrowed from browser/modules/test/browser_taskbar_preview.js
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    var version = Components.classes["@mozilla.org/system-info;1"]
-                            .getService(Components.interfaces.nsIPropertyBag2)
+    var version = SpecialPowers.Cc["@mozilla.org/system-info;1"]
+                            .getService(SpecialPowers.Ci.nsIPropertyBag2)
                             .getProperty("version");
     kIsWindowsVistaOrHigher = (parseFloat(version) >= 6.0);
   }
 
   function getEnv(env) {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    var envsvc = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment);
+    var envsvc = SpecialPowers.Cc["@mozilla.org/process/environment;1"].getService(SpecialPowers.Ci.nsIEnvironment);
     var val = envsvc.get(env);
     if (val == "")
       return null;
     return val;
   }
 
   var reportType = WebGLTestHarnessModule.TestHarness.reportType;
 
--- a/content/events/test/test_bug457672.html
+++ b/content/events/test/test_bug457672.html
@@ -12,16 +12,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=457672">Mozilla Bug 457672</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 457672 **/
 
 var windowBlurCount = 0;
 
 function setUserPref(reset) {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   const prefSvcContractID = "@mozilla.org/preferences-service;1";
   const prefSvcIID = Components.interfaces.nsIPrefService;
rename from content/html/content/src/nsHTMLIFrameElement.cpp
rename to content/html/content/src/HTMLIFrameElement.cpp
--- a/content/html/content/src/nsHTMLIFrameElement.cpp
+++ b/content/html/content/src/HTMLIFrameElement.cpp
@@ -1,104 +1,104 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
-#include "nsHTMLIFrameElement.h"
+#include "mozilla/dom/HTMLIFrameElement.h"
 #include "nsIDOMSVGDocument.h"
 #include "nsMappedAttributes.h"
 #include "nsAttrValueInlines.h"
 #include "nsError.h"
 #include "nsRuleData.h"
 #include "nsStyleConsts.h"
 #include "nsContentUtils.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
-
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
 
-nsHTMLIFrameElement::nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
-                                         FromParser aFromParser)
+DOMCI_NODE_DATA(HTMLIFrameElement, mozilla::dom::HTMLIFrameElement)
+
+namespace mozilla {
+namespace dom {
+
+HTMLIFrameElement::HTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                                     FromParser aFromParser)
   : nsGenericHTMLFrameElement(aNodeInfo, aFromParser)
 {
 }
 
-nsHTMLIFrameElement::~nsHTMLIFrameElement()
+HTMLIFrameElement::~HTMLIFrameElement()
 {
 }
 
-NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameElement, Element)
-NS_IMPL_RELEASE_INHERITED(nsHTMLIFrameElement, Element)
-
-DOMCI_NODE_DATA(HTMLIFrameElement, nsHTMLIFrameElement)
+NS_IMPL_ADDREF_INHERITED(HTMLIFrameElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLIFrameElement, Element)
 
-// QueryInterface implementation for nsHTMLIFrameElement
-NS_INTERFACE_TABLE_HEAD(nsHTMLIFrameElement)
-  NS_HTML_CONTENT_INTERFACE_TABLE_BEGIN(nsHTMLIFrameElement)
-    NS_INTERFACE_TABLE_ENTRY(nsHTMLIFrameElement, nsIDOMHTMLIFrameElement)
-    NS_INTERFACE_TABLE_ENTRY(nsHTMLIFrameElement, nsIDOMGetSVGDocument)
+// QueryInterface implementation for HTMLIFrameElement
+NS_INTERFACE_TABLE_HEAD(HTMLIFrameElement)
+  NS_HTML_CONTENT_INTERFACE_TABLE_BEGIN(HTMLIFrameElement)
+    NS_INTERFACE_TABLE_ENTRY(HTMLIFrameElement, nsIDOMHTMLIFrameElement)
+    NS_INTERFACE_TABLE_ENTRY(HTMLIFrameElement, nsIDOMGetSVGDocument)
   NS_OFFSET_AND_INTERFACE_TABLE_END
-  NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLIFrameElement,
+  NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(HTMLIFrameElement,
                                                nsGenericHTMLFrameElement)
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLIFrameElement)
 
-NS_IMPL_ELEMENT_CLONE(nsHTMLIFrameElement)
+NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement)
 
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Align, align)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, FrameBorder, frameborder)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Height, height)
-NS_IMPL_URI_ATTR(nsHTMLIFrameElement, LongDesc, longdesc)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, MarginHeight, marginheight)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, MarginWidth, marginwidth)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Name, name)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Scrolling, scrolling)
-NS_IMPL_URI_ATTR(nsHTMLIFrameElement, Src, src)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Width, width)
-NS_IMPL_BOOL_ATTR(nsHTMLIFrameElement, Allowfullscreen, allowfullscreen)
-NS_IMPL_STRING_ATTR(nsHTMLIFrameElement, Sandbox, sandbox)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Align, align)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, FrameBorder, frameborder)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Height, height)
+NS_IMPL_URI_ATTR(HTMLIFrameElement, LongDesc, longdesc)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, MarginHeight, marginheight)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, MarginWidth, marginwidth)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Name, name)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Scrolling, scrolling)
+NS_IMPL_URI_ATTR(HTMLIFrameElement, Src, src)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Width, width)
+NS_IMPL_BOOL_ATTR(HTMLIFrameElement, AllowFullscreen, allowfullscreen)
+NS_IMPL_STRING_ATTR(HTMLIFrameElement, Sandbox, sandbox)
 
 void
-nsHTMLIFrameElement::GetItemValueText(nsAString& aValue)
+HTMLIFrameElement::GetItemValueText(nsAString& aValue)
 {
   GetSrc(aValue);
 }
 
 void
-nsHTMLIFrameElement::SetItemValueText(const nsAString& aValue)
+HTMLIFrameElement::SetItemValueText(const nsAString& aValue)
 {
   SetSrc(aValue);
 }
 
 NS_IMETHODIMP
-nsHTMLIFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
+HTMLIFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
 {
   return nsGenericHTMLFrameElement::GetContentDocument(aContentDocument);
 }
 
 NS_IMETHODIMP
-nsHTMLIFrameElement::GetContentWindow(nsIDOMWindow** aContentWindow)
+HTMLIFrameElement::GetContentWindow(nsIDOMWindow** aContentWindow)
 {
   return nsGenericHTMLFrameElement::GetContentWindow(aContentWindow);
 }
 
 NS_IMETHODIMP
-nsHTMLIFrameElement::GetSVGDocument(nsIDOMDocument **aResult)
+HTMLIFrameElement::GetSVGDocument(nsIDOMDocument **aResult)
 {
   return GetContentDocument(aResult);
 }
 
 bool
-nsHTMLIFrameElement::ParseAttribute(int32_t aNamespaceID,
-                                    nsIAtom* aAttribute,
-                                    const nsAString& aValue,
-                                    nsAttrValue& aResult)
+HTMLIFrameElement::ParseAttribute(int32_t aNamespaceID,
+                                  nsIAtom* aAttribute,
+                                  const nsAString& aValue,
+                                  nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::marginwidth) {
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::marginheight) {
       return aResult.ParseSpecialIntValue(aValue);
     }
@@ -175,17 +175,17 @@ MapAttributesIntoRule(const nsMappedAttr
   }
 
   nsGenericHTMLElement::MapScrollingAttributeInto(aAttributes, aData);
   nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData);
   nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
 }
 
 NS_IMETHODIMP_(bool)
-nsHTMLIFrameElement::IsAttributeMapped(const nsIAtom* aAttribute) const
+HTMLIFrameElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 {
   static const MappedAttributeEntry attributes[] = {
     { &nsGkAtoms::width },
     { &nsGkAtoms::height },
     { &nsGkAtoms::frameborder },
     { nullptr },
   };
 
@@ -197,25 +197,25 @@ nsHTMLIFrameElement::IsAttributeMapped(c
   };
   
   return FindAttributeDependence(aAttribute, map);
 }
 
 
 
 nsMapRuleToAttributesFunc
-nsHTMLIFrameElement::GetAttributeMappingFunction() const
+HTMLIFrameElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 nsresult
-nsHTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                                  const nsAttrValue* aValue,
-                                  bool aNotify)
+HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue,
+                                bool aNotify)
 {
   if (aName == nsGkAtoms::sandbox && aNameSpaceID == kNameSpaceID_None) {
     // Parse the new value of the sandbox attribute, and if we have a docshell
     // set its sandbox flags appropriately.
     if (mFrameLoader) {
       nsCOMPtr<nsIDocShell> docshell = mFrameLoader->GetExistingDocShell();
 
       if (docshell) {
@@ -232,19 +232,22 @@ nsHTMLIFrameElement::AfterSetAttr(int32_
       }
     }
   }
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                             aNotify);
 }
 
 uint32_t
-nsHTMLIFrameElement::GetSandboxFlags()
+HTMLIFrameElement::GetSandboxFlags()
 {
   nsAutoString sandboxAttr;
 
   if (GetAttr(kNameSpaceID_None, nsGkAtoms::sandbox, sandboxAttr)) {
     return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
   }
 
   // No sandbox attribute, no sandbox flags.
   return 0;
 }
+
+} // namespace dom
+} // namespace mozilla
rename from content/html/content/src/nsHTMLIFrameElement.h
rename to content/html/content/src/HTMLIFrameElement.h
--- a/content/html/content/src/nsHTMLIFrameElement.h
+++ b/content/html/content/src/HTMLIFrameElement.h
@@ -1,27 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifndef mozilla_dom_HTMLIFrameElement_h
+#define mozilla_dom_HTMLIFrameElement_h
+
 #include "nsGenericHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMGetSVGDocument.h"
 
-class nsHTMLIFrameElement : public nsGenericHTMLFrameElement
-                          , public nsIDOMHTMLIFrameElement
-                          , public nsIDOMGetSVGDocument
+namespace mozilla {
+namespace dom {
+
+class HTMLIFrameElement MOZ_FINAL : public nsGenericHTMLFrameElement
+                                  , public nsIDOMHTMLIFrameElement
+                                  , public nsIDOMGetSVGDocument
 {
 public:
-  nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
-                      mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
-  virtual ~nsHTMLIFrameElement();
+  HTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                    FromParser aFromParser = NOT_FROM_PARSER);
+  virtual ~HTMLIFrameElement();
 
-  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(nsHTMLIFrameElement, iframe)
+  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLIFrameElement, iframe)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
   // nsIDOMElement
@@ -53,8 +59,13 @@ public:
                                 bool aNotify);
 
   uint32_t GetSandboxFlags();
 
 protected:
   virtual void GetItemValueText(nsAString& text);
   virtual void SetItemValueText(const nsAString& text);
 };
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -16,17 +16,16 @@ LIBXUL_LIBRARY	= 1
 ifndef _MSC_VER
 FAIL_ON_WARNINGS = 1
 endif # !_MSC_VER
 
 
 EXPORTS		= \
 		HTMLPropertiesCollection.h \
 		nsGenericHTMLElement.h \
-		nsHTMLIFrameElement.h \
 		nsClientRect.h \
 		nsHTMLDNSPrefetch.h \
 		nsTimeRanges.h \
 		$(NULL)
 
 EXPORTS_NAMESPACES = mozilla/dom
 
 EXPORTS_mozilla/dom = \
@@ -38,16 +37,17 @@ EXPORTS_mozilla/dom = \
 		HTMLDataListElement.h \
 		HTMLDivElement.h \
 		HTMLFieldSetElement.h \
 		HTMLFontElement.h \
 		HTMLFrameElement.h \
 		HTMLFrameSetElement.h \
 		HTMLHeadingElement.h \
 		HTMLHRElement.h \
+		HTMLIFrameElement.h \
 		HTMLImageElement.h \
 		HTMLLabelElement.h \
 		HTMLLegendElement.h \
 		HTMLLIElement.h \
 		HTMLLinkElement.h \
 		HTMLMapElement.h \
 		HTMLMenuElement.h \
 		HTMLMenuItemElement.h \
@@ -98,17 +98,17 @@ CPPSRCS		= \
 		HTMLDivElement.cpp \
 		HTMLFieldSetElement.cpp \
 		HTMLFontElement.cpp \
 		nsHTMLFormElement.cpp \
 		HTMLFrameElement.cpp \
 		HTMLFrameSetElement.cpp \
 		HTMLHRElement.cpp \
 		HTMLHeadingElement.cpp \
-		nsHTMLIFrameElement.cpp \
+		HTMLIFrameElement.cpp \
 		HTMLImageElement.cpp \
 		nsHTMLInputElement.cpp \
 		HTMLLIElement.cpp \
 		HTMLLabelElement.cpp \
 		HTMLLegendElement.cpp \
 		HTMLLinkElement.cpp \
 		HTMLMapElement.cpp \
 		HTMLMenuElement.cpp \
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2354,16 +2354,19 @@ nsresult nsHTMLMediaElement::FinishDecod
   // The new stream has not been suspended by us.
   mPausedForInactiveDocumentOrChannel = false;
   mEventDeliveryPaused = false;
   mPendingEvents.Clear();
 
   aDecoder->SetAudioChannelType(mAudioChannelType);
   aDecoder->SetAudioCaptured(mAudioCaptured);
   aDecoder->SetVolume(mMuted ? 0.0 : mVolume);
+  aDecoder->SetPreservesPitch(mPreservesPitch);
+  aDecoder->SetPlaybackRate(mPlaybackRate);
+
   for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
     OutputMediaStream* ms = &mOutputStreams[i];
     aDecoder->AddOutputStream(ms->mStream->GetStream()->AsProcessedStream(),
         ms->mFinishWhenEnded);
   }
 
   nsresult rv = aDecoder->Load(aStream, aListener, aCloneDonor);
   if (NS_FAILED(rv)) {
@@ -3576,17 +3579,19 @@ NS_IMETHODIMP nsHTMLMediaElement::GetMoz
 {
   *aPreservesPitch = mPreservesPitch;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::SetMozPreservesPitch(bool aPreservesPitch)
 {
   mPreservesPitch = aPreservesPitch;
-  mDecoder->SetPreservesPitch(aPreservesPitch);
+  if (mDecoder) {
+    mDecoder->SetPreservesPitch(mPreservesPitch);
+  }
   return NS_OK;
 }
 
 ImageContainer* nsHTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -250,16 +250,18 @@ MOCHITEST_FILES = \
 		file_fullscreen-denied.html \
 		file_fullscreen-denied-inner.html \
 		file_fullscreen-hidden.html \
 		file_fullscreen-navigation.html \
 		file_fullscreen-esc-exit.html \
 		file_fullscreen-esc-exit-inner.html \
 		file_fullscreen-rollback.html \
 		file_fullscreen-svg-element.html \
+		file_fullscreen-multiple.html \
+		file_fullscreen-multiple-inner.html \
 		test_li_attributes_reflection.html \
 		test_link_attributes_reflection.html \
 		test_ol_attributes_reflection.html \
 		test_dl_attributes_reflection.html \
 		test_ul_attributes_reflection.html \
 		test_param_attributes_reflection.html \
 		test_base_attributes_reflection.html \
 		test_dir_attributes_reflection.html \
--- a/content/html/content/test/file_fullscreen-api.html
+++ b/content/html/content/test/file_fullscreen-api.html
@@ -73,17 +73,17 @@ function enter1(event) {
   is(document.mozFullScreenElement, null, "8. Full-screen element should still be null after re-adding former FSE.");
 }
 
 function exit1(event) {
   ok(!document.mozFullScreen, "9. Should have left full-screen mode (first time)");
   is(event.target, document, "10. Event target should be full-screen document #2");
   is(document.mozFullScreenElement, null, "11. Full-screen element should be null.");
   iframe = document.createElement("iframe");
-  iframe.allowfullscreen = true;
+  iframe.allowFullscreen = true;
   addFullscreenChangeContinuation("enter", enter2);
   document.body.appendChild(iframe);
   iframe.src = iframeContents;
 }
 
 function enter2(event) {
   ok(document.mozFullScreen, "12. Should be back in full-screen mode (second time)");
   is(event.target, document, "13. Event target should be full-screen document #3");
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_fullscreen-multiple-inner.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 724554</title>
+  <script type="application/javascript" src="file_fullscreen-utils.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+
+/** Test for Bug 545812 **/
+function begin(id) {
+  addFullscreenErrorContinuation(function() {
+    opener.ok(false, "Fullscreen denied " + id);
+    });
+  addFullscreenChangeContinuation("enter",
+    function() {
+      opener.enteredFullscreen(id);
+    });
+  document.body.mozRequestFullScreen();
+}
+
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_fullscreen-multiple.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=724554
+
+Test that multiple windows can be fullscreen at the same time.
+
+Open one window, focus it and enter fullscreen, then open another, focus
+it and enter fullscreen, and check that both are still fullscreen.
+
+-->
+<head>
+  <title>Test for Bug 724554</title>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="file_fullscreen-utils.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+/** Test for Bug 545812 **/
+
+function ok(condition, msg) {
+  opener.ok(condition, "[multiple] " + msg);
+}
+
+function is(a, b, msg) {
+  opener.is(a, b, "[multiple] " + msg);
+}
+
+var window1, window2;
+
+function openWindow(id) {
+  var w = window.open("file_fullscreen-multiple-inner.html", "", "width=500,height=500");
+  w.addEventListener("load", function onload() {
+    w.focus();
+    SimpleTest.waitForFocus(function(){w.begin(id)}, w);
+  });
+  return w;
+}
+
+function begin() {
+  window1 = openWindow("one");
+}
+
+function enteredFullscreen(id) {
+  if (id == "one") {
+    window2 = openWindow("two");
+  } else if (id == "two") {
+    ok(window1.document.mozFullScreenElement &&
+       window2.document.mozFullScreenElement,
+       "Both windows should be fullscreen concurrently");
+    window1.close();
+    window2.close();
+    opener.nextTest();
+  }
+}
+
+</script>
+</pre>
+<div id="full-screen-element"></div>
+</body>
+</html>
--- a/content/html/content/test/forms/test_meter_element.html
+++ b/content/html/content/test/forms/test_meter_element.html
@@ -16,16 +16,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="visibility: hidden;">
   <form id='f' method='get' target='submit_frame' action='foo'>
     <meter id='m' value=0.5></meter>
   </form>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+} else {
+  SimpleTest.expectAssertions(1);
+}
+
 /** Test for <meter> **/
 
 function checkFormIDLAttribute(aElement)
 {
   is('form' in aElement, false, "<meter> shouldn't have a form attribute");
 }
 
 function checkAttribute(aElement, aAttribute, aNewValue, aExpectedValueForIDL)
--- a/content/html/content/test/forms/test_progress_element.html
+++ b/content/html/content/test/forms/test_progress_element.html
@@ -18,16 +18,18 @@ and
 <div id="content" style="visibility: hidden;">
   <form id='f' method='get' target='submit_frame' action='foo'>
     <progress id='p'></progress>
   </form>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for progress element content and layout **/
 
 function checkFormIDLAttribute(aElement)
 {
   is("form" in aElement, false, "<progress> shouldn't have a form attribute");
 }
 
 function checkAttribute(aElement, aAttribute, aNewValue, aExpectedValueForIDL)
--- a/content/html/content/test/test_bug242709.html
+++ b/content/html/content/test/test_bug242709.html
@@ -12,16 +12,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=242709">Mozilla Bug 242709</a>
 <p id="display"></p>
 <div id="content">
 <iframe src="bug242709_iframe.html" id="a"></iframe> 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 242709 **/
 
 SimpleTest.waitForExplicitFinish();
 
 var submitted = function() {
   ok(true, "Disabling button after form submission doesn't prevent submitting");
   SimpleTest.finish();
 }
--- a/content/html/content/test/test_bug277890.html
+++ b/content/html/content/test/test_bug277890.html
@@ -12,16 +12,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=277890">Mozilla Bug 277890</a>
 <p id="display"></p>
 <div id="content">
 <iframe src="bug277890_iframe.html" id="a"></iframe> 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 277890 **/
 
 SimpleTest.waitForExplicitFinish();
 
 var submitted = function() {
   ok(true, "Disabling button after form submission doesn't prevent submitting");
   SimpleTest.finish();
 }
--- a/content/html/content/test/test_bug523771.html
+++ b/content/html/content/test/test_bug523771.html
@@ -14,16 +14,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 <iframe name="target_iframe" id="target_iframe"></iframe>
 <form action="form_submit_server.sjs" target="target_iframe" id="form"
 method="POST" enctype="multipart/form-data">
   <input id=singleFile name=singleFile type=file>
   <input id=multiFile name=multiFile type=file multiple>
 </form>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+} else {
+  SimpleTest.expectAssertions(1);
+}
+
 var filesToKill = [];
 singleFileInput = document.getElementById('singleFile');
 multiFileInput = document.getElementById('multiFile');
 var input1File = { name: "523771_file1", type: "", body: "file1 contents"};
 var input2Files =
   [{ name: "523771_file2", type: "", body: "second file contents" },
    { name: "523771_file3.txt", type: "text/plain", body: "123456" },
    { name: "523771_file4.html", type: "text/html", body: "<html>content</html>" }
--- a/content/html/content/test/test_fullscreen-api.html
+++ b/content/html/content/test/test_fullscreen-api.html
@@ -29,16 +29,17 @@ SpecialPowers.setBoolPref("full-screen-a
 // Disable the requirement for trusted contexts only, so the tests are easier
 // to write.
 SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
 
 // Run the tests which go full-screen in new windows, as mochitests normally
 // run in an iframe, which by default will not have the allowfullscreen
 // attribute set, so full-screen won't work.
 var gTestWindows = [
+  "file_fullscreen-multiple.html",
   "file_fullscreen-rollback.html",
   "file_fullscreen-esc-exit.html",
   "file_fullscreen-denied.html",
   "file_fullscreen-api.html",
   "file_fullscreen-api-keys.html",
   "file_fullscreen-plugins.html",
   "file_fullscreen-hidden.html",
   "file_fullscreen-svg-element.html",
--- a/content/html/content/test/test_iframe_sandbox_general.html
+++ b/content/html/content/test/test_iframe_sandbox_general.html
@@ -7,16 +7,19 @@ Implement HTML5 sandbox attribute for IF
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <script type="application/javascript">
+
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs - general tests **/
 
 SimpleTest.waitForExplicitFinish();
 
 // a postMessage handler that is used by sandboxed iframes without
 // 'allow-same-origin' to communicate pass/fail back to this main page.
 // it expects to be called with an object like {ok: true/false, desc:
 // <description of the test> which it then forwards to ok()
--- a/content/html/content/test/test_iframe_sandbox_navigation.html
+++ b/content/html/content/test/test_iframe_sandbox_navigation.html
@@ -7,16 +7,19 @@ Implement HTML5 sandbox attribute for IF
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604 - navigation</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 	<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <script type="application/javascript">
+
+SimpleTest.expectAssertions(1, 2);
+
 /** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
 /** Navigation tests **/
 
 SimpleTest.waitForExplicitFinish();
 // a postMessage handler that is used by sandboxed iframes without
 // 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
 // it expects to be called with an object like {ok: true/false, desc:
 // <description of the test> which it then forwards to ok()
--- a/content/html/document/test/test_bug391777.html
+++ b/content/html/document/test/test_bug391777.html
@@ -8,16 +8,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/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=391777">Mozilla Bug 391777</a>
 <p id="display"></p>
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Win") != 0) {
+  // not Windows
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /** Test for Bug 391777 **/
 var arg = {};
 arg.testVal = "foo";
 var result = window.showModalDialog("javascript:window.returnValue = window.dialogArguments.testVal; window.close(); 'This window should close on its own.';", arg);
 ok(true, "We should get here without user interaction");
 is(result, "foo", "Unexpected result from showModalDialog");
 </script>
 </body>
--- a/content/html/document/test/test_bug402680.html
+++ b/content/html/document/test/test_bug402680.html
@@ -16,16 +16,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content">
   <input type="text">
   <textarea></textarea>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Mac") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /** Test for Bug 402680 **/
 
 ok(activeElementIsNull,
    "Before document has body, active element should be null");
 
 function testActiveElement() {
   ok(document.body == document.activeElement,
      "After page load body element should be the active element!");
--- a/content/html/document/test/test_bug403868.html
+++ b/content/html/document/test/test_bug403868.html
@@ -12,16 +12,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=403868">Mozilla Bug 403868</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Mac") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /** Test for Bug 403868 **/
 function createSpan(id, insertionPoint) {
   var s = document.createElement("span");
   s.id = id;
   $("content").insertBefore(s, insertionPoint);
   return s;
 }
 
--- a/content/html/document/test/test_bug446483.html
+++ b/content/html/document/test/test_bug446483.html
@@ -12,16 +12,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=446483">Mozilla Bug 446483</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") != 0) {
+  // not Windows
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /** Test for Bug 446483 **/
 
 function gc() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
         .getInterface(Components.interfaces.nsIDOMWindowUtils)
         .garbageCollect();
 }
--- a/content/html/document/test/test_bug448564.html
+++ b/content/html/document/test/test_bug448564.html
@@ -15,16 +15,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <iframe src="bug448564-iframe-2.html"></iframe>
   <iframe src="bug448564-iframe-3.html"></iframe>
 </p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(3);
+
 /** Test for Bug 448564 **/
 
 /**
  * The three iframes are going to be loaded with some dirty constructed forms.
  * Each of them will be submitted before the load event and a SJS will replace
  * the frame content with the query string.
  * Then, on the load event, our test file will check the content of each iframes
  * and check if the query string were correctly formatted (implying that all
--- a/content/html/document/test/test_bug478251.html
+++ b/content/html/document/test/test_bug478251.html
@@ -12,16 +12,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478251">Mozilla Bug 478251</a>
 <p id="display"><iframe id="t"></iframe></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+SimpleTest.expectAssertions(10);
+
 /** Test for Bug 478251 **/
 var doc = $("t").contentDocument;
 doc.open();
 doc.write();
 doc.close();
 is(doc.documentElement.textContent, "", "Writing || failed");
 
 doc.open();
--- a/content/html/document/test/test_bug741266.html
+++ b/content/html/document/test/test_bug741266.html
@@ -13,16 +13,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=741266">Mozilla Bug 741266</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+SimpleTest.expectAssertions(1);
+
 /** Test for Bug 741266 **/
 var w = window.open("", "", "width=100,height=100");
 is(w.innerHeight, 100, "Popup height should be 100 when opened with window.open");
 // XXXbz On at least some platforms, the innerWidth is off by the scrollbar
 // width for some reason.  So just make sure it's the same for both popups.
 var width = w.innerWidth;
 w.close();
 w = document.open("", "", "width=100,height=100");
--- a/content/html/document/test/test_document-element-inserted.html
+++ b/content/html/document/test/test_document-element-inserted.html
@@ -5,16 +5,21 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <iframe id = 'media'>
 </iframe>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 4);
+}
+
 SimpleTest.waitForExplicitFinish();
 var loc;
 
 var observe = function(doc){
     if (doc == media.contentDocument) {
         ok(media.contentDocument.location.toString().indexOf(loc) != -1,
           "The loaded media should be " + loc);
         next();
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -322,16 +322,18 @@ bool MediaDecoder::IsInfinite()
   return mInfiniteStream;
 }
 
 MediaDecoder::MediaDecoder() :
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mCurrentTime(0.0),
   mInitialVolume(0.0),
+  mInitialPlaybackRate(1.0),
+  mInitialPreservesPitch(true),
   mRequestedSeekTime(-1.0),
   mDuration(-1),
   mTransportSeekable(true),
   mMediaSeekable(true),
   mReentrantMonitor("media.decoder"),
   mPlayState(PLAY_STATE_PAUSED),
   mNextState(PLAY_STATE_PAUSED),
   mCalledResourceLoaded(false),
@@ -463,16 +465,18 @@ nsresult MediaDecoder::InitializeStateMa
   }
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mDecoderStateMachine->SetTransportSeekable(mTransportSeekable);
     mDecoderStateMachine->SetMediaSeekable(mMediaSeekable);
     mDecoderStateMachine->SetDuration(mDuration);
     mDecoderStateMachine->SetVolume(mInitialVolume);
     mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured);
+    SetPlaybackRate(mInitialPlaybackRate);
+    mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch);
 
     if (mFrameBufferLength > 0) {
       // The valid mFrameBufferLength value was specified earlier
       mDecoderStateMachine->SetFrameBufferLength(mFrameBufferLength);
     }
   }
 
   ChangeState(PLAY_STATE_LOADING);
@@ -1396,23 +1400,27 @@ void MediaDecoder::SetPlaybackRate(doubl
     if (mOwner && !mOwner->GetPaused()) {
       Play();
     }
     mPausedForPlaybackRateNull = false;
   }
 
   if (mDecoderStateMachine) {
     mDecoderStateMachine->SetPlaybackRate(aPlaybackRate);
+  } else {
+    mInitialPlaybackRate = aPlaybackRate;
   }
 }
 
 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
 {
   if (mDecoderStateMachine) {
     mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
+  } else {
+    mInitialPreservesPitch = aPreservesPitch;
   }
 }
 
 bool MediaDecoder::OnDecodeThread() const {
   NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
   return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeThread() : false;
 }
 
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -896,16 +896,21 @@ public:
   // video (if it is a video) or the callback period of the audio.
   // It is read and written from the main thread only.
   double mCurrentTime;
 
   // Volume that playback should start at.  0.0 = muted. 1.0 = full
   // volume.  Readable/Writeable from the main thread.
   double mInitialVolume;
 
+  // PlaybackRate and pitch preservation status we should start at.
+  // Readable/Writeable from the main thread.
+  double mInitialPlaybackRate;
+  bool mInitialPreservesPitch;
+
   // Position to seek to when the seek notification is received by the
   // decode thread. Written by the main thread and read via the
   // decode thread. Synchronised using mReentrantMonitor. If the
   // value is negative then no seek has been requested. When a seek is
   // started this is reset to negative.
   double mRequestedSeekTime;
 
   // Duration of the media resource. Set to -1 if unknown.
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/844563.html
@@ -0,0 +1,5 @@
+<script>
+var a = document.createElementNS("http://www.w3.org/1999/xhtml", "audio");
+a.mozPreservesPitch = a;
+</script>
+
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -7,8 +7,9 @@ HTTP load 481136-1.html # needs to be HT
 load 493915-1.html
 skip-if(Android) load 495794-1.html
 load 492286-1.xhtml
 load 576612-1.html
 skip-if(Android) load 691096-1.html # Android sound API can't handle playing large number of sounds at once.
 load 752784-1.html
 skip-if(Android||B2G) HTTP load 795892-1.html # load failed, bug 833371 for B2G
 skip-if(Android||B2G) load 789075-1.html # load failed, bug 833371 for B2G
+load 844563.html
--- a/content/media/test/test_bug495300.html
+++ b/content/media/test/test_bug495300.html
@@ -10,16 +10,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495300">Mozilla Bug 495300</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Mac") != 0) {
+  // not Mac
+  SimpleTest.expectAssertions(0, 1);
+}
+
 var manager = new MediaTestManager;
 
 function filename(uri) {
   return uri.substr(uri.lastIndexOf("/")+1);
 }
 
 function mediaEnded(event) {
   ok(true, "Got expected 'ended' event: " + filename(event.target.currentSrc));
--- a/content/media/test/test_bug686942.html
+++ b/content/media/test/test_bug686942.html
@@ -10,16 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686942">Mozilla Bug 686942</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 var manager = new MediaTestManager;
 
 function onloaded(event) {
   var v = event.target;
   v.currentTime = v.duration;
   return;
 }
 
--- a/content/media/test/test_playback_rate.html
+++ b/content/media/test/test_playback_rate.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type='application/javascript;version=1.8'>
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 let manager = new MediaTestManager;
 
 function rangeCheck(lhs, rhs, threshold) {
   var diff = Math.abs(lhs - rhs);
   if (diff < threshold) {
     return true;
   }
   return false;
--- a/content/media/test/test_seek.html
+++ b/content/media/test/test_seek.html
@@ -18,16 +18,18 @@
   <script type="text/javascript" src="seek11.js"></script>
   <script type="text/javascript" src="seek12.js"></script>
   <script type="text/javascript" src="seek13.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(120, 400);
+
 var manager = new MediaTestManager;
 
 const NUM_SEEK_TESTS = 13;
 
 function createTestArray() {
   var tests = [];
   var tmpVid = document.createElement("video");
 
--- a/content/media/test/test_seek_out_of_range.html
+++ b/content/media/test/test_seek_out_of_range.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 var manager = new MediaTestManager;
 
 // Test if the ended event works correctly.
 
 function startTest(e) {
   var v = e.target;
   checkMetadata(v._name, v, v._test);
   is(v._loadedMetadata, false, "Should only receive one loadedmetadata event for " + v._name);
--- a/content/xbl/test/test_bug378518.xul
+++ b/content/xbl/test/test_bug378518.xul
@@ -40,16 +40,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <box id="myExtendedBox" command="myExtendedBoxCommand"
     style="-moz-binding:url(#mybinding)">
     <label>myExtendedBox</label>
   </box>
 
   <!-- test code goes here -->
   <script type="application/javascript"> <![CDATA[
 
+    SimpleTest.expectAssertions(1);
+
     var myBoxClicked = false;
     var myCheckBoxClicked = false;
     var myExtendedBoxClicked = false;
 
     function testClick(elemName) {
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       var wu  =
         window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
--- a/content/xbl/test/test_bug397934.xhtml
+++ b/content/xbl/test/test_bug397934.xhtml
@@ -73,17 +73,17 @@ addLoadEvent(function() {
      "test1" + ": First field wins even if undefined");
   is(typeof(d.emptyTest1), "undefined",
      "test1" + ": First field wins even if undefined, double-check");
   is(d.emptyTest2, "empty2",
      "test1" + ": First field wins");
   is(d.testAncestor, "ancestor",
      "test1" + ": Empty field should not override ancestor binding");
 
-  var win = XPCNativeWrapper.unwrap(window);
+  var win = window;
   win.counter = 0;
   d.testEvalOnce;
   d.testEvalOnce;
   is(win.counter, 1, "Field should have evaluated once and only once");
 
   d = $("display2");
   is(d.storage, undefined,
      "test2" +
--- a/content/xul/templates/tests/chrome/test_tmpl_listboxelement.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_listboxelement.xul
@@ -44,16 +44,18 @@
       <listcell label=""/>
     </listitem>
   </data>
 
 <script src="templates_shared.js"/>
 
 <script>
 <![CDATA[
+SimpleTest.expectAssertions(4);
+
 SimpleTest.waitForExplicitFinish();
 
 var testid ="listbox element";
 var queryType = "rdf";
 var isTreeBuilder = false;
 var needsOpen = false;
 var notWorkingYet = false;
 var notWorkingYetDynamic = false;
--- a/content/xul/templates/tests/chrome/test_tmpl_storage_dynamicparameters.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_storage_dynamicparameters.xul
@@ -21,16 +21,18 @@
     <listitem anyid="true" label="Emu"/>
     <listitem anyid="true" label="Raven"/>
   </data>
 
 <script src="templates_shared.js"/>
 
 <script>
 <![CDATA[
+SimpleTest.expectAssertions(1, 2);
+
 SimpleTest.waitForExplicitFinish();
 
 copyToProfile('animals.sqlite');
 
 function test_storage_template()
 {
   var root = document.getElementById("root");
   expectedOutput = document.getElementById("output");
--- a/docshell/test/chrome/test_bug303267.xul
+++ b/docshell/test/chrome/test_bug303267.xul
@@ -23,16 +23,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+SimpleTest.expectAssertions(0, 1);
+
 /** Test for Bug 303267 **/
 
 SimpleTest.waitForExplicitFinish();
 window.open("bug303267_window.xul", "bug303267",
             "chrome,width=600,height=600");
 
 ]]>
 </script>
--- a/docshell/test/chrome/test_bug456980.xul
+++ b/docshell/test/chrome/test_bug456980.xul
@@ -15,16 +15,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- 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=456980"
      target="_blank">Mozilla Bug 396519</a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
+    SimpleTest.expectAssertions(1);
+
     SimpleTest.waitForExplicitFinish();
 
     addLoadEvent(function() {
       window.open("bug113934_window.xul?chrome", "bug456980",
                   "chrome,width=800,height=800");
     });
 
   ]]></script>
--- a/docshell/test/chrome/test_bug608669.xul
+++ b/docshell/test/chrome/test_bug608669.xul
@@ -34,16 +34,17 @@ function setCachePref(enabled) {
     gOrigMaxTotalViewers = undefined;
     try {
       prefBranch.clearUserPref("browser.sessionhistory.cache_subframes");
     } catch (e) { /* Pref didn't exist, meh */ }
   }
 }
 
 
+SimpleTest.expectAssertions(1);
 
     /** Test for Bug 608669 **/
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(nextTest);
 
 gen = doTest();
 
--- a/docshell/test/chrome/test_mozFrameType.xul
+++ b/docshell/test/chrome/test_mozFrameType.xul
@@ -22,16 +22,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /** Test for Bug 769771 **/
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(function () {
   window.open("mozFrameType_window.xul", "mozFrameType",
               "chrome,width=600,height=600");
 });
 
--- a/docshell/test/navigation/test_child.html
+++ b/docshell/test/navigation/test_child.html
@@ -4,16 +4,20 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+if (navigator.platform.indexOf("Mac") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 window.onload = function() {
   navigateByLocation(frames[0]);
   navigateByOpen("child1");
   navigateByForm("child2");
   navigateByHyperlink("child3");
 
   xpcWaitForFinishedFrames(function() {
     isNavigated(frames[0], "Should be able to navigate off-domain child by setting location.");
--- a/docshell/test/navigation/test_grandchild.html
+++ b/docshell/test/navigation/test_grandchild.html
@@ -4,16 +4,20 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 200px; }
     </style>
 <script>
+if (navigator.platform.indexOf("Mac") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 window.onload = function () {
   navigateByLocation(frames[0].frames[0]);
   navigateByOpen("child1_child0");
   navigateByForm("child2_child0");
   navigateByHyperlink("child3_child0");
 
   xpcWaitForFinishedFrames(function() {
     isNavigated(frames[0].frames[0], "Should be able to navigate off-domain grandchild by setting location.");
--- a/docshell/test/navigation/test_reserved.html
+++ b/docshell/test/navigation/test_reserved.html
@@ -4,16 +4,20 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 200px; }
     </style>
 <script>
+if (navigator.platform.indexOf("Mac") == 0) {
+  SimpleTest.expectAssertions(0, 2);
+}
+
 function testTop() {
   window0 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#top,location", "_blank", "width=10,height=10");
 
   xpcWaitForFinishedFrames(function() {
     isInaccessible(window0, "Should be able to navigate off-domain top by setting location.");
     window0.close();
     xpcCleanupWindows();
 
--- a/docshell/test/test_bug580069.html
+++ b/docshell/test/test_bug580069.html
@@ -10,16 +10,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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=580069">Mozilla Bug 580069</a>
 
 <iframe id='iframe' src='file_bug580069_1.html'></iframe>
 
 <script type="application/javascript">
+
+SimpleTest.expectAssertions(1);
+
 SimpleTest.waitForExplicitFinish();
 
 var iframe = document.getElementById('iframe');
 var iframeCw = iframe.contentWindow;
 
 // Called when file_bug580069_1.html loads.
 function page1Load() {
   // This should cause us to load file 2.
--- a/docshell/test/test_bug668513.html
+++ b/docshell/test/test_bug668513.html
@@ -3,16 +3,20 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=668513
 -->
 <head>
   <title>Test for Bug 668513</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 <script>
+if (navigator.platform.indexOf("Linux") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   function onload_test()
   {
     var win = frames[0];
     ok(win.performance, 'Window.performance should be defined');
     ok(win.performance.navigation, 'Window.performance.navigation should be defined');
     var navigation = win.performance && win.performance.navigation;
     if (navigation === undefined)
     {
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -130,16 +130,24 @@ AudioChannelService::UnregisterType(Audi
   // The array may contain multiple occurrence of this appId but
   // this should remove only the first one.
   AudioChannelInternalType type = GetInternalType(aType, aElementHidden);
   mChannelCounters[type].RemoveElement(aChildID);
 
   // In order to avoid race conditions, it's safer to notify any existing
   // agent any time a new one is registered.
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    // We only remove ChildID when it is in the foreground.
+    // If in the background, we kept ChildID for allowing it to play next song.
+    if (aType == AUDIO_CHANNEL_CONTENT &&
+        mActiveContentChildIDs.Contains(aChildID) &&
+        (!aElementHidden &&
+         !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID))) {
+      mActiveContentChildIDs.RemoveElement(aChildID);
+    }
     SendAudioChannelChangedNotification();
     Notify();
   }
 }
 
 bool
 AudioChannelService::GetMuted(AudioChannelAgent* aAgent, bool aElementHidden)
 {
@@ -170,33 +178,41 @@ AudioChannelService::GetMutedInternal(Au
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 
   // If the audio content channel is visible, let's remember this ChildID.
   if (newType == AUDIO_CHANNEL_INT_CONTENT &&
-      oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-      !mActiveContentChildIDs.Contains(aChildID)) {
+      oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN) {
 
     if (mActiveContentChildIDsFrozen) {
       mActiveContentChildIDsFrozen = false;
       mActiveContentChildIDs.Clear();
     }
 
-    mActiveContentChildIDs.AppendElement(aChildID);
+    if (!mActiveContentChildIDs.Contains(aChildID)) {
+      mActiveContentChildIDs.AppendElement(aChildID);
+    }
   }
 
-  // If nothing is visible, the list has to been frozen.
   else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
            oldType == AUDIO_CHANNEL_INT_CONTENT &&
-           !mActiveContentChildIDsFrozen &&
-           mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-    mActiveContentChildIDsFrozen = true;
+           !mActiveContentChildIDsFrozen) {
+    // If nothing is visible, the list has to been frozen.
+    // Or if there is still any one with other ChildID in foreground then
+    // it should be removed from list and left other ChildIDs in the foreground
+    // to keep playing. Finally only last one childID which go to background
+    // will be in list.
+    if (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
+      mActiveContentChildIDsFrozen = true;
+    } else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
+      mActiveContentChildIDs.RemoveElement(aChildID);
+    }
   }
 
   if (newType != oldType && aType == AUDIO_CHANNEL_CONTENT) {
     Notify();
   }
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
@@ -286,18 +302,22 @@ AudioChannelService::SendAudioChannelCha
     else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM_HIDDEN].IsEmpty()) {
       higher = AUDIO_CHANNEL_ALARM;
     }
 
     else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
       higher = AUDIO_CHANNEL_NOTIFICATION;
     }
 
-    // Content channels play in background if just one is active.
-    else if (!mActiveContentChildIDs.IsEmpty()) {
+    // There is only one Child can play content channel in the background.
+    // And need to check whether there is any content channels under playing
+    // now.
+    else if (!mActiveContentChildIDs.IsEmpty() &&
+             mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(
+             mActiveContentChildIDs[0])) {
       higher = AUDIO_CHANNEL_CONTENT;
     }
   }
 
   if (higher != mCurrentHigherChannel) {
     mCurrentHigherChannel = higher;
 
     nsString channelName;
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -1,11 +1,16 @@
 /* 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/. */
+#ifdef XP_WIN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
 
 #include "TestHarness.h"
 
 #include "AudioChannelService.h"
 #include "AudioChannelAgent.h"
 
 #define TEST_ENSURE_BASE(_test, _msg)       \
   PR_BEGIN_MACRO                            \
@@ -14,29 +19,35 @@
       return NS_ERROR_FAILURE;              \
     } else {                                \
       passed(_msg);                         \
     }                                       \
   PR_END_MACRO
 
 using namespace mozilla::dom;
 
-class Agent
+class Agent : public nsIAudioChannelAgentCallback
 {
 public:
+  NS_DECL_ISUPPORTS
+
   Agent(AudioChannelType aType)
   : mType(aType)
+  , mWaitCallback(false)
   , mRegistered(false)
+  , mCanPlay(false)
   {
     mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
   }
 
+  virtual ~Agent() {}
+
   nsresult Init()
   {
-    nsresult rv = mAgent->Init(mType, nullptr);
+    nsresult rv = mAgent->Init(mType, this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return mAgent->SetVisibilityState(false);
   }
 
   nsresult StartPlaying(bool *_ret)
   {
     if (mRegistered)
@@ -45,310 +56,375 @@ public:
     nsresult rv = mAgent->StartPlaying(_ret);
     mRegistered = true;
     return rv;
   }
 
   nsresult StopPlaying()
   {
     mRegistered = false;
+    int loop = 0;
+    while (mWaitCallback) {
+      #ifdef XP_WIN
+      Sleep(1000);
+      #else
+      sleep(1);
+      #endif
+      if (loop++ == 5) {
+        TEST_ENSURE_BASE(false, "StopPlaying timeout");
+      }
+    }
     return mAgent->StopPlaying();
   }
 
+  nsresult SetVisibilityState(bool visible)
+  {
+    if (mRegistered) {
+      mWaitCallback = true;
+    }
+    return mAgent->SetVisibilityState(visible);
+  }
+
+  NS_IMETHODIMP CanPlayChanged(bool canPlay)
+  {
+    mCanPlay = canPlay;
+    mWaitCallback = false;
+    return NS_OK;
+  }
+
+  nsresult GetCanPlay(bool *_ret)
+  {
+    int loop = 0;
+    while (mWaitCallback) {
+      #ifdef XP_WIN
+      Sleep(1000);
+      #else
+      sleep(1);
+      #endif
+      if (loop++ == 5) {
+        TEST_ENSURE_BASE(false, "GetCanPlay timeout");
+      }
+    }
+    *_ret = mCanPlay;
+    return NS_OK;
+  }
+
   nsCOMPtr<AudioChannelAgent> mAgent;
   AudioChannelType mType;
+  bool mWaitCallback;
   bool mRegistered;
+  bool mCanPlay;
 };
 
+NS_IMPL_ISUPPORTS1(Agent, nsIAudioChannelAgentCallback)
+
 nsresult
 TestDoubleStartPlaying()
 {
-  Agent agent(AUDIO_CHANNEL_NORMAL);
-  nsresult rv = agent.Init();
+  nsCOMPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+
+  nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool playing;
-  rv = agent.mAgent->StartPlaying(&playing);
+  rv = agent->mAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent.mAgent->StartPlaying(&playing);
+  rv = agent->mAgent->StartPlaying(&playing);
   TEST_ENSURE_BASE(NS_FAILED(rv), "Test0: StartPlaying calling twice should return error");
 
   return NS_OK;
 }
 
 nsresult
 TestOneNormalChannel()
 {
-  Agent agent(AUDIO_CHANNEL_NORMAL);
-  nsresult rv = agent.Init();
+  nsCOMPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool playing;
-  rv = agent.StartPlaying(&playing);
+  rv = agent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test1: A normal channel unvisible agent must not be playing");
 
-  rv = agent.mAgent->SetVisibilityState(true);
+  rv = agent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent.StartPlaying(&playing);
+  rv = agent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test1: A normal channel visible agent should be playing");
 
   return rv;
 }
 
 nsresult
 TestTwoNormalChannels()
 {
-  Agent agent1(AUDIO_CHANNEL_NORMAL);
-  nsresult rv = agent1.Init();
+  nsCOMPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent agent2(AUDIO_CHANNEL_NORMAL);
-  rv = agent2.Init();
+  nsCOMPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_NORMAL);
+  rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool playing;
-  rv = agent1.StartPlaying(&playing);
+  rv = agent1->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test2: A normal channel unvisible agent1 must not be playing");
 
-  rv = agent2.StartPlaying(&playing);
+  rv = agent2->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test2: A normal channel unvisible agent2 must not be playing");
 
-  rv = agent1.mAgent->SetVisibilityState(true);
+  rv = agent1->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent2.mAgent->SetVisibilityState(true);
+  rv = agent2->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent1.StartPlaying(&playing);
+  rv = agent1->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test2: A normal channel visible agent1 should be playing");
 
-  rv = agent2.StartPlaying(&playing);
+  rv = agent2->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test2: A normal channel visible agent2 should be playing");
 
   return rv;
 }
 
 nsresult
 TestContentChannels()
 {
-  Agent agent1(AUDIO_CHANNEL_CONTENT);
-  nsresult rv = agent1.Init();
+  nsCOMPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent agent2(AUDIO_CHANNEL_CONTENT);
-  rv = agent2.Init();
+  nsCOMPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
+  rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent1.mAgent->SetVisibilityState(true);
+  /* All content channels in the foreground can be allowed to play */
+  rv = agent1->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent2.mAgent->SetVisibilityState(true);
+  rv = agent2->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool playing;
-  rv = agent1.StartPlaying(&playing);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing");
-
-  rv = agent2.StartPlaying(&playing);
-  NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
-
-  rv = agent1.mAgent->SetVisibilityState(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent2.mAgent->SetVisibilityState(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = agent1.StartPlaying(&playing);
+  rv = agent1->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing");
 
-  rv = agent2.StartPlaying(&playing);
+  rv = agent2->StartPlaying(&playing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent2 should be playing");
+
+  /* Test the transition state of one content channel tried to set non-visible
+   * state first when app is going to background. */
+  rv = agent1->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
 
-  rv = agent1.mAgent->SetVisibilityState(true);
+  rv = agent1->GetCanPlay(&playing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing from foreground to background");
+
+  /* Test all content channels set non-visible already */
+  rv = agent2->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent2.mAgent->SetVisibilityState(true);
+  rv = agent2->GetCanPlay(&playing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing from foreground to background");
+
+  /* Clear the content channels & mActiveContentChildIDs in AudioChannelService.
+   * If agent stop playing in the background, we will reserve it's childID in
+   * mActiveContentChildIDs, then it can allow to play next song. So we set agents
+   * to foreground first then stopping to play */
+  rv = agent1->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = agent2->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = agent1->StopPlaying();
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = agent2->StopPlaying();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent1.StartPlaying(&playing);
+  /* Test that content channels can't be allow to play when they starts from the background state */
+  rv = agent1->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing");
+  rv = agent2->SetVisibilityState(false);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = agent2.StartPlaying(&playing);
+  rv = agent1->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent2 should be playing");
+  TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent1 must not be playing from background state");
+
+  rv = agent2->StartPlaying(&playing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent2 must not be playing from background state");
 
   return rv;
 }
 
 nsresult
 TestPriorities()
 {
-  Agent normalAgent(AUDIO_CHANNEL_NORMAL);
-  nsresult rv = normalAgent.Init();
+  nsCOMPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsresult rv = normalAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent contentAgent(AUDIO_CHANNEL_CONTENT);
-  rv = contentAgent.Init();
+  nsCOMPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
+  rv = contentAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent notificationAgent(AUDIO_CHANNEL_NOTIFICATION);
-  rv = notificationAgent.Init();
+  nsCOMPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
+  rv = notificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent alarmAgent(AUDIO_CHANNEL_ALARM);
-  rv = alarmAgent.Init();
+  nsCOMPtr<Agent> alarmAgent = new Agent(AUDIO_CHANNEL_ALARM);
+  rv = alarmAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent telephonyAgent(AUDIO_CHANNEL_TELEPHONY);
-  rv = telephonyAgent.Init();
+  nsCOMPtr<Agent> telephonyAgent = new Agent(AUDIO_CHANNEL_TELEPHONY);
+  rv = telephonyAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent ringerAgent(AUDIO_CHANNEL_RINGER);
-  rv = ringerAgent.Init();
+  nsCOMPtr<Agent> ringerAgent = new Agent(AUDIO_CHANNEL_RINGER);
+  rv = ringerAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Agent pNotificationAgent(AUDIO_CHANNEL_PUBLICNOTIFICATION);
-  rv = pNotificationAgent.Init();
+  nsCOMPtr<Agent> pNotificationAgent = new Agent(AUDIO_CHANNEL_PUBLICNOTIFICATION);
+  rv = pNotificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool playing;
 
   // Normal should not be playing because not visible.
-  rv = normalAgent.StartPlaying(&playing);
+  rv = normalAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(!playing, "Test4: A normal channel unvisible agent should not be playing");
+  TEST_ENSURE_BASE(!playing, "Test4: A normal channel unvisible agent must not be playing");
 
   // Content should be playing.
-  rv = contentAgent.StartPlaying(&playing);
+  rv = contentAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playing, "Test4: A content channel unvisible agent should be playing");
+  TEST_ENSURE_BASE(!playing, "Test4: A content channel unvisible agent must not be playing from background state");
 
   // Notification should be playing.
-  rv = notificationAgent.StartPlaying(&playing);
+  rv = notificationAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: An notification channel unvisible agent should be playing");
 
   // Now content should be not playing because of the notification playing.
-  rv = contentAgent.StartPlaying(&playing);
+  rv = contentAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test4: A content channel unvisible agent should not be playing when notification channel is playing");
 
   // Adding an alarm.
-  rv = alarmAgent.StartPlaying(&playing);
+  rv = alarmAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: An alarm channel unvisible agent should be playing");
 
   // Now notification should be not playing because of the alarm playing.
-  rv = notificationAgent.StartPlaying(&playing);
+  rv = notificationAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test4: A notification channel unvisible agent should not be playing when an alarm is playing");
 
   // Adding an telephony.
-  rv = telephonyAgent.StartPlaying(&playing);
+  rv = telephonyAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: An telephony channel unvisible agent should be playing");
 
   // Now alarm should be not playing because of the telephony playing.
-  rv = alarmAgent.StartPlaying(&playing);
+  rv = alarmAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test4: A alarm channel unvisible agent should not be playing when a telephony is playing");
 
   // Adding an ringer.
-  rv = ringerAgent.StartPlaying(&playing);
+  rv = ringerAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: An ringer channel unvisible agent should be playing");
 
   // Now telephony should be not playing because of the ringer playing.
-  rv = telephonyAgent.StartPlaying(&playing);
+  rv = telephonyAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test4: A telephony channel unvisible agent should not be playing when a riger is playing");
 
   // Adding an pNotification.
-  rv = pNotificationAgent.StartPlaying(&playing);
+  rv = pNotificationAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: An pNotification channel unvisible agent should be playing");
 
   // Now ringer should be not playing because of the public notification playing.
-  rv = ringerAgent.StartPlaying(&playing);
+  rv = ringerAgent->StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(!playing, "Test4: A ringer channel unvisible agent should not be playing when a public notification is playing");
 
   // Settings visible the normal channel.
-  rv = normalAgent.mAgent->SetVisibilityState(true);
+  rv = normalAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Normal should be playing because visible.
-  rv = normalAgent.StartPlaying(&playing);
+  rv = normalAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A normal channel visible agent should be playing");
 
   // Settings visible the content channel.
-  rv = contentAgent.mAgent->SetVisibilityState(true);
+  rv = contentAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Content should be playing because visible.
-  rv = contentAgent.StartPlaying(&playing);
+  rv = contentAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A content channel visible agent should be playing");
 
   // Settings visible the notification channel.
-  rv = notificationAgent.mAgent->SetVisibilityState(true);
+  rv = notificationAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Notification should be playing because visible.
-  rv = notificationAgent.StartPlaying(&playing);
+  rv = notificationAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A notification channel visible agent should be playing");
 
   // Settings visible the alarm channel.
-  rv = alarmAgent.mAgent->SetVisibilityState(true);
+  rv = alarmAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Alarm should be playing because visible.
-  rv = alarmAgent.StartPlaying(&playing);
+  rv = alarmAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A alarm channel visible agent should be playing");
 
   // Settings visible the telephony channel.
-  rv = telephonyAgent.mAgent->SetVisibilityState(true);
+  rv = telephonyAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Telephony should be playing because visible.
-  rv = telephonyAgent.StartPlaying(&playing);
+  rv = telephonyAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A telephony channel visible agent should be playing");
 
   // Settings visible the ringer channel.
-  rv = ringerAgent.mAgent->SetVisibilityState(true);
+  rv = ringerAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Ringer should be playing because visible.
-  rv = ringerAgent.StartPlaying(&playing);
+  rv = ringerAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A ringer channel visible agent should be playing");
 
   // Settings visible the pNotification channel.
-  rv = pNotificationAgent.mAgent->SetVisibilityState(true);
+  rv = pNotificationAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // pNotification should be playing because visible.
-  rv = pNotificationAgent.StartPlaying(&playing);
+  rv = pNotificationAgent->GetCanPlay(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test4: A pNotification channel visible agent should be playing");
 
   return rv;
 }
 
 int main(int argc, char** argv)
 {
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/844559.html
@@ -0,0 +1,3 @@
+<script>
+Components.lookupMethod(Image, "x")
+</script>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -34,8 +34,9 @@ load 693894.html
 load 693811-1.html
 load 693811-2.html
 load 693811-3.html
 load 695867.html
 load 697643.html
 load 706283-1.html
 load 708405-1.html
 load 745495.html
+load 844559.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2953,17 +2953,17 @@ nsDOMWindowUtils::RemoteFrameFullscreenR
 
 nsresult
 nsDOMWindowUtils::ExitFullscreen()
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsIDocument::ExitFullScreen(/* async = */ false);
+  nsIDocument::ExitFullscreen(nullptr, /* async */ false);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SelectAtPoint(float aX, float aY, uint32_t aSelectBehavior,
                                 bool *_retval)
 {
   *_retval = false;
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1161,17 +1161,17 @@ nsFocusManager::SetFocusInner(nsIContent
   if (contentToFocus &&
       nsContentUtils::GetRootDocument(contentToFocus->OwnerDoc())->IsFullScreenDoc() &&
       nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     "DOM",
                                     contentToFocus->OwnerDoc(),
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "FocusedWindowedPluginWhileFullScreen");
-    nsIDocument::ExitFullScreen(true);
+    nsIDocument::ExitFullscreen(contentToFocus->OwnerDoc(), /* async */ true);
   }
 #endif
 
   // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
   // shifted away from the current element if the new shell to focus is
   // the same or an ancestor shell of the currently focused shell.
   bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
                             IsSameOrAncestor(newWindow, mFocusedWindow);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4726,17 +4726,17 @@ nsGlobalWindow::SetFullScreenInternal(bo
   FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
 
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
   bool rootWinFullScreen;
   GetFullScreen(&rootWinFullScreen);
   // Only chrome can change our fullScreen mode, unless we're running in
   // untrusted mode.
-  if (aFullScreen == rootWinFullScreen || 
+  if (aFullScreen == rootWinFullScreen ||
       (aRequireTrust && !nsContentUtils::IsCallerChrome())) {
     return NS_OK;
   }
 
   // SetFullScreen needs to be called on the root window, so get that
   // via the DocShell tree, and if we are not already the root,
   // call SetFullScreen on that window instead.
   nsCOMPtr<nsIDocShellTreeItem> rootItem;
@@ -4787,17 +4787,18 @@ nsGlobalWindow::SetFullScreenInternal(bo
       widget->MakeFullScreen(aFullScreen);
   }
 
   if (!mFullScreen) {
     // Force exit from DOM full-screen mode. This is so that if we're in
     // DOM full-screen mode and the user exits full-screen mode with
     // the browser full-screen mode toggle keyboard-shortcut, we'll detect
     // that and leave DOM API full-screen mode too.
-    nsIDocument::ExitFullScreen(false);
+    nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
+    nsIDocument::ExitFullscreen(doc, /* async */ false);
   }
 
   if (!mWakeLock && mFullScreen) {
     nsCOMPtr<nsIPowerManagerService> pmService =
       do_GetService(POWERMANAGERSERVICE_CONTRACTID);
     NS_ENSURE_TRUE(pmService, NS_OK);
 
     pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"), this, getter_AddRefs(mWakeLock));
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/BindingGen.py
@@ -1,66 +1,73 @@
 # 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/.
 
 import os
 import cPickle
 from Configuration import Configuration
-from Codegen import CGBindingRoot, replaceFileIfChanged
+from Codegen import CGBindingRoot
 
-def generate_binding_header(config, outputprefix, webidlfile):
+def generate_binding_header(config, outputprefix, srcprefix, webidlfile):
     """
     |config| Is the configuration object.
     |outputprefix| is a prefix to use for the header guards and filename.
     """
 
     filename = outputprefix + ".h"
+    depsname = ".deps/" + filename + ".pp"
     root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.declare()):
-        print "Generating binding header: %s" % (filename)
+    with open(filename, 'wb') as f:
+        f.write(root.declare())
+    with open(depsname, 'wb') as f:
+        f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps()))
 
-def generate_binding_cpp(config, outputprefix, webidlfile):
+def generate_binding_cpp(config, outputprefix, srcprefix, webidlfile):
     """
     |config| Is the configuration object.
     |outputprefix| is a prefix to use for the header guards and filename.
     """
 
     filename = outputprefix + ".cpp"
+    depsname = ".deps/" + filename + ".pp"
     root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.define()):
-        print "Generating binding implementation: %s" % (filename)
+    with open(filename, 'wb') as f:
+        f.write(root.define())
+    with open(depsname, 'wb') as f:
+        f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps()))
 
 def main():
 
     # Parse arguments.
     from optparse import OptionParser
-    usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile"
+    usagestring = "usage: %prog [header|cpp] configFile outputPrefix srcPrefix webIDLFile"
     o = OptionParser(usage=usagestring)
     o.add_option("--verbose-errors", action='store_true', default=False,
                  help="When an error happens, display the Python traceback.")
     (options, args) = o.parse_args()
 
-    if len(args) != 4 or (args[0] != "header" and args[0] != "cpp"):
+    if len(args) != 5 or (args[0] != "header" and args[0] != "cpp"):
         o.error(usagestring)
     buildTarget = args[0]
     configFile = os.path.normpath(args[1])
     outputPrefix = args[2]
-    webIDLFile = os.path.normpath(args[3])
+    srcPrefix = os.path.normpath(args[3])
+    webIDLFile = os.path.normpath(args[4])
 
     # Load the parsing results
     f = open('ParserResults.pkl', 'rb')
     parserData = cPickle.load(f)
     f.close()
 
     # Create the configuration data.
     config = Configuration(configFile, parserData)
 
     # Generate the prototype classes.
     if buildTarget == "header":
-        generate_binding_header(config, outputPrefix, webIDLFile);
+        generate_binding_header(config, outputPrefix, srcPrefix, webIDLFile);
     elif buildTarget == "cpp":
-        generate_binding_cpp(config, outputPrefix, webIDLFile);
+        generate_binding_cpp(config, outputPrefix, srcPrefix, webIDLFile);
     else:
         assert False # not reached
 
 if __name__ == '__main__':
     main()
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -54,16 +54,19 @@ class CGThing():
     def __init__(self):
         pass # Nothing for now
     def declare(self):
         """Produce code for a header file."""
         assert(False)  # Override me!
     def define(self):
         """Produce code for a cpp file."""
         assert(False) # Override me!
+    def deps(self):
+        """Produce the deps for a pp file"""
+        assert(False) # Override me!
 
 class CGNativePropertyHooks(CGThing):
     """
     Generate a NativePropertyHooks for a given descriptor
     """
     def __init__(self, descriptor, properties):
         CGThing.__init__(self)
         self.descriptor = descriptor
@@ -304,29 +307,38 @@ class CGList(CGThing):
     def prepend(self, child):
         self.children.insert(0, child)
     def join(self, generator):
         return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
     def declare(self):
         return self.join(child.declare() for child in self.children if child is not None)
     def define(self):
         return self.join(child.define() for child in self.children if child is not None)
+    def deps(self):
+        deps = set()
+        for child in self.children:
+            if child is None:
+                continue
+            deps = deps.union(child.deps())
+        return deps
 
 class CGGeneric(CGThing):
     """
     A class that spits out a fixed string into the codegen.  Can spit out a
     separate string for the declaration too.
     """
     def __init__(self, define="", declare=""):
         self.declareText = declare
         self.defineText = define
     def declare(self):
         return self.declareText
     def define(self):
         return self.defineText
+    def deps(self):
+        return set()
 
 # We'll want to insert the indent at the beginnings of lines, but we
 # don't want to indent empty lines.  So only indent lines that have a
 # non-newline character on them.
 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
 class CGIndenter(CGThing):
     """
     A class that takes another CGThing and generates code that indents that
@@ -382,16 +394,19 @@ class CGWrapper(CGThing):
         defn = self.child.define()
         if self.reindent:
             # We don't use lineStartDetector because we don't want to
             # insert whitespace at the beginning of our _first_ line.
             defn = stripTrailingWhitespace(
                 defn.replace("\n", "\n" + (" " * len(self.definePre))))
         return self.definePre + defn + self.definePost
 
+    def deps(self):
+        return self.child.deps()
+
 class CGIfWrapper(CGWrapper):
     def __init__(self, child, condition):
         pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n",
                         reindent=True)
         CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
                            post="\n}")
 
 class CGNamespace(CGWrapper):
@@ -980,24 +995,40 @@ class CGNamedConstructors(CGThing):
     def __init__(self, descriptor):
         self.descriptor = descriptor
         CGThing.__init__(self)
     def declare(self):
         return ""
     def define(self):
         if len(self.descriptor.interface.namedConstructors) == 0:
             return ""
+
+        constructorID = "constructors::id::"
+        if self.descriptor.interface.hasInterfaceObject():
+            constructorID += self.descriptor.name
+        else:
+            constructorID += "_ID_Count"
+        nativePropertyHooks = """const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
+    nullptr,
+    nullptr,
+    { nullptr, nullptr },
+    prototypes::id::%s,
+    %s,
+    nullptr
+};
+
+""" % (self.descriptor.name, constructorID)
         namedConstructors = CGList([], ",\n")
         for n in self.descriptor.interface.namedConstructors:
-            namedConstructors.append(CGGeneric("{ \"%s\", { %s, nullptr }, %i }" % (n.identifier.name, NamedConstructorName(n), methodLength(n))))
+            namedConstructors.append(CGGeneric("{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i }" % (n.identifier.name, NamedConstructorName(n), methodLength(n))))
         namedConstructors.append(CGGeneric("{ nullptr, { nullptr, nullptr }, 0 }"))
         namedConstructors = CGWrapper(CGIndenter(namedConstructors),
                                       pre="static const NamedConstructor namedConstructors[] = {\n",
                                       post="\n};\n")
-        return namedConstructors.define()
+        return nativePropertyHooks + namedConstructors.define()
 
 class CGClassHasInstanceHook(CGAbstractStaticMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
                 Argument('JSMutableHandleValue', 'vp'), Argument('JSBool*', 'bp')]
         CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
                                         'JSBool', args)
 
@@ -4848,16 +4879,19 @@ class CGEnum(CGThing):
         return """
   const EnumEntry strings[%d] = {
     %s,
     { NULL, 0 }
   };
 """ % (len(self.enum.values()) + 1,
        ",\n    ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
 
+    def deps(self):
+        return self.enum.getDeps()
+
 def getUnionAccessorSignatureType(type, descriptorProvider):
     """
     Returns the types that are used in the getter and setter signatures for
     union types
     """
     if type.isArray():
         raise TypeError("Can't handle array arguments yet")
 
@@ -5738,39 +5772,45 @@ class CGPrototypeTraitsClass(CGClass):
         templateSpecialization = ['prototypes::id::' + descriptor.name]
         enums = [ClassEnum('', ['Depth'],
                            [descriptor.interface.inheritanceDepth()])]
         typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
         CGClass.__init__(self, 'PrototypeTraits', indent=indent,
                          templateArgs=templateArgs,
                          templateSpecialization=templateSpecialization,
                          enums=enums, typedefs=typedefs, isStruct=True)
+    def deps(self):
+        return set()
 
 class CGPrototypeIDMapClass(CGClass):
     def __init__(self, descriptor, indent=''):
         templateArgs = [Argument('class', 'ConcreteClass')]
         templateSpecialization = [descriptor.nativeType]
         enums = [ClassEnum('', ['PrototypeID'],
                            ['prototypes::id::' + descriptor.name])]
         CGClass.__init__(self, 'PrototypeIDMap', indent=indent,
                          templateArgs=templateArgs,
                          templateSpecialization=templateSpecialization,
                          enums=enums, isStruct=True)
+    def deps(self):
+        return set()
 
 class CGClassForwardDeclare(CGThing):
     def __init__(self, name, isStruct=False):
         CGThing.__init__(self)
         self.name = name
         self.isStruct = isStruct
     def declare(self):
         type = 'struct' if self.isStruct else 'class'
         return '%s %s;\n' % (type, self.name)
     def define(self):
         # Header only
         return ''
+    def deps(self):
+        return set()
 
 class CGProxySpecialOperation(CGPerSignatureCall):
     """
     Base class for classes for calling an indexed or named special operation
     (don't use this directly, use the derived classes below).
     """
     def __init__(self, descriptor, operation):
         nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
@@ -6437,16 +6477,18 @@ def stripTrailingWhitespace(text):
     return '\n'.join(lines) + tail
 
 class CGDescriptor(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
 
         assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
 
+        self._deps = descriptor.interface.getDeps()
+
         cgThings = []
         # These are set to true if at least one non-static
         # method/getter/setter exist on the interface.
         (hasMethod, hasGetter, hasLenientGetter,
          hasSetter, hasLenientSetter) = False, False, False, False, False
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
                                                NamedConstructorName(n)))
@@ -6573,16 +6615,18 @@ class CGDescriptor(CGThing):
         self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
                                             cgThings),
                                 post='\n')
 
     def declare(self):
         return self.cgRoot.declare()
     def define(self):
         return self.cgRoot.define()
+    def deps(self):
+        return self._deps
 
 class CGNamespacedEnum(CGThing):
     def __init__(self, namespace, enumName, names, values, comment=""):
 
         if not values:
             values = []
 
         # Account for explicit enum values.
@@ -6777,16 +6821,19 @@ class CGDictionary(CGThing):
                 "initMembers": "\n\n".join(memberInits),
                 "idInit": CGIndenter(idinit).define(),
                 "isMainThread": toStringBool(not self.workers),
                 "toObjectParent": CGIndenter(CGGeneric(toObjectParent)).define(),
                 "ensureObject": CGIndenter(CGGeneric(ensureObject)).define(),
                 "defineMembers": "\n\n".join(memberDefines)
                 })
 
+    def deps(self):
+        return self.dictionary.getDeps()
+
     @staticmethod
     def makeDictionaryName(dictionary, workers):
         suffix = "Workers" if workers else ""
         return dictionary.identifier.name + suffix
 
     def makeClassName(self, dictionary):
         return self.makeDictionaryName(dictionary, self.workers)
 
@@ -7198,16 +7245,19 @@ class CGBindingRoot(CGThing):
         # Store the final result.
         self.root = curr
 
     def declare(self):
         return stripTrailingWhitespace(self.root.declare())
     def define(self):
         return stripTrailingWhitespace(self.root.define())
 
+    def deps(self):
+        return self.root.deps()
+
 class CGNativeMember(ClassMethod):
     def __init__(self, descriptor, member, name, signature, extendedAttrs,
                  breakAfter=True, passCxAsNeeded=True, visibility="public",
                  jsObjectsArePtr=False, variadicIsSequence=False):
         """
         If jsObjectsArePtr is true, typed arrays and "object" will be
         passed as JSObject*
         """
@@ -7773,16 +7823,17 @@ class CGExampleRoot(CGThing):
 
     def define(self):
         return self.root.define()
 
 class CGCallback(CGClass):
     def __init__(self, idlObject, descriptorProvider, baseName, methods,
                  getters=[], setters=[]):
         self.baseName = baseName
+        self._deps = idlObject.getDeps()
         name = idlObject.identifier.name
         if descriptorProvider.workers:
             name += "Workers"
         # For our public methods that needThisHandling we want most of the
         # same args and the same return type as what CallbackMember
         # generates.  So we want to take advantage of all its
         # CGNativeMember infrastructure, but that infrastructure can't deal
         # with templates and most especially template arguments.  So just
@@ -7862,16 +7913,19 @@ class CGCallback(CGClass):
                             bodyInHeader=True,
                             templateArgs=["typename T"],
                             body=bodyWithThis),
                 ClassMethod(method.name, method.returnType, argsWithoutThis,
                             bodyInHeader=True,
                             body=bodyWithoutThis),
                 method]
 
+    def deps(self):
+        return self._deps
+
 class CGCallbackFunction(CGCallback):
     def __init__(self, callback, descriptorProvider):
         CGCallback.__init__(self, callback, descriptorProvider,
                             "CallbackFunction",
                             methods=[CallCallback(callback, descriptorProvider)])
 
     def getConstructors(self):
         return CGCallback.getConstructors(self) + [
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -60,22 +60,24 @@ class Configuration:
                 d.nativeType != descriptor.nativeType or d == descriptor
                 for d in self.descriptors)
 
         self.enums = [e for e in parseData if e.isEnum()]
 
         # Figure out what our main-thread and worker dictionaries and callbacks
         # are.
         mainTypes = set()
-        for descriptor in self.getDescriptors(workers=False, isExternal=False):
+        for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] +
+                           self.getDescriptors(workers=False, isExternal=False, skipGen=False)):
             mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
         (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes)
 
         workerTypes = set();
-        for descriptor in self.getDescriptors(workers=True, isExternal=False):
+        for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] +
+                           self.getDescriptors(workers=True, isExternal=False, skipGen=False)):
             workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
         (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes)
 
         self.dictionaries = [d for d in parseData if d.isDictionary()]
         self.callbacks = [c for c in parseData if
                           c.isCallback() and not c.isInterface()]
 
         def flagWorkerOrMainThread(items, main, worker):
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -70,17 +70,16 @@ EXPORTS_$(binding_include_path) = \
   NonRefcountedDOMObject.h \
   Nullable.h \
   PrimitiveConversions.h \
   PrototypeList.h \
   RegisterBindings.h \
   TypedArray.h \
   UnionConversions.h \
   UnionTypes.h \
-  $(exported_binding_headers) \
   $(NULL)
 
 LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \
   -I$(topsrcdir)/js/xpconnect/wrappers \
   -I$(topsrcdir)/content/canvas/src \
   -I$(topsrcdir)/content/html/content/src \
   -I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
   -I$(topsrcdir)/dom/base \
@@ -92,27 +91,52 @@ LOCAL_INCLUDES += -I$(topsrcdir)/js/xpco
   $(NULL)
 
 ifdef MOZ_AUDIO_CHANNEL_MANAGER
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/dom/system/gonk \
   $(NULL)
 endif
 
+# XXXkhuey this is a terrible hack to avoid blowing out the command line
+ifneq (,$(filter-out all chrome default export realchrome tools clean clobber clobber_all distclean realclean,$(MAKECMDGOALS)))
+$(shell echo "$(addsuffix .pp,$(binding_header_files))" > pp.list)
+$(shell echo "$(addsuffix .pp,$(binding_cpp_files))" >> pp.list)
+
+# The script mddepend.pl checks the dependencies and writes to stdout
+# one rule to force out-of-date objects. For example,
+#   foo.o boo.o: FORCE
+# The script has an advantage over including the *.pp files directly
+# because it handles the case when header files are removed from the build.
+# 'make' would complain that there is no way to build missing headers.
+ALL_PP_RESULTS = $(shell cat pp.list | $(PERL) $(BUILD_TOOLS)/mddepend.pl)
+$(eval $(ALL_PP_RESULTS))
+
+endif
+
+EXPORTS_GENERATED_FILES := $(exported_binding_headers)
+EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path)
+EXPORTS_GENERATED_TARGET := webidl-export
+INSTALL_TARGETS += EXPORTS_GENERATED
+
 include $(topsrcdir)/config/rules.mk
 
+# We need to create a separate target so we can ensure that the pickle is
+# done before generating headers.
+export:: ParserResults.pkl
+	$(MAKE) webidl-export
+
 # If you change bindinggen_dependencies here, change it in
 # dom/bindings/test/Makefile.in too.
 bindinggen_dependencies := \
   BindingGen.py \
   Bindings.conf \
   Configuration.py \
   Codegen.py \
   parser/WebIDL.py \
-  ParserResults.pkl \
   $(GLOBAL_DEPS) \
   $(NULL)
 
 CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \
                        $(topsrcdir)/layout/style/nsCSSPropAliasList.h \
                        $(webidl_base)/CSS2Properties.webidl.in \
                        $(webidl_base)/CSS2PropertiesProps.h \
                        $(srcdir)/GenerateCSS2PropertiesWebIDL.py \
@@ -124,30 +148,36 @@ CSS2Properties.webidl: $(topsrcdir)/layo
 $(webidl_files): %: $(webidl_base)/%
 	$(INSTALL) $(IFLAGS1) $(webidl_base)/$* .
 
 $(test_webidl_files): %: $(srcdir)/test/%
 	$(INSTALL) $(IFLAGS1) $(srcdir)/test/$* .
 
 $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
                                      %.webidl \
+                                     $(call mkdir_deps,$(MDDEPDIR)) \
                                      $(NULL)
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/BindingGen.py header \
-	  $(srcdir)/Bindings.conf $*Binding \
+	  $(srcdir)/Bindings.conf \
+	  $*Binding \
+	  $(topsrcdir)/dom/webidl/ \
 	  $*.webidl
 
 $(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
                                     %.webidl \
+                                    $(call mkdir_deps,$(MDDEPDIR)) \
                                     $(NULL)
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/BindingGen.py cpp \
-	  $(srcdir)/Bindings.conf $*Binding \
+	  $(srcdir)/Bindings.conf \
+	  $*Binding \
+	  $(topsrcdir)/dom/webidl/ \
 	  $*.webidl
 
 $(globalgen_targets): ParserResults.pkl
 
 %-example: $(bindinggen_dependencies) \
            $(all_webidl_files) \
            $(NULL)
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
@@ -190,9 +220,11 @@ GARBAGE += \
   $(wildcard *-example.h) \
   $(wildcard *-example.cpp) \
   $(NULL)
 
 # Make sure all binding header files are created during the export stage, so we
 # don't have issues with .cpp files being compiled before we've generated the
 # headers they depend on.  This is really only needed for the test files, since
 # the non-test headers are all exported above anyway.
-export:: $(binding_header_files)
+webidl-export:: $(binding_header_files)
+
+.PHONY: webidl-export
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -169,16 +169,48 @@ class IDLObject(object):
         self.userData[key] = value
 
     def addExtendedAttributes(self, attrs):
         assert False # Override me!
 
     def handleExtendedAttribute(self, attr):
         assert False # Override me!
 
+    def _getDependentObjects(self):
+        assert False # Override me!
+
+    def getDeps(self, visited=None):
+        """ Return a set of files that this object depends on.  If any of
+            these files are changed the parser needs to be rerun to regenerate
+            a new IDLObject.
+
+            The visited argument is a set of all the objects already visited.
+            We must test to see if we are in it, and if so, do nothing.  This
+            prevents infinite recursion."""
+
+        # NB: We can't use visited=set() above because the default value is
+        # evaluated when the def statement is evaluated, not when the function
+        # is executed, so there would be one set for all invocations.
+        if visited == None:
+            visited = set()
+
+        if self in visited:
+            return set()
+
+        visited.add(self)
+
+        deps = set()
+        if self.filename() != "<builtin>":
+            deps.add(self.filename())
+
+        for d in self._getDependentObjects():
+            deps = deps.union(d.getDeps(visited))
+
+        return deps
+
 class IDLScope(IDLObject):
     def __init__(self, location, parentScope, identifier):
         IDLObject.__init__(self, location)
 
         self.parentScope = parentScope
         if identifier:
             assert isinstance(identifier, IDLIdentifier)
             self._name = identifier
@@ -438,16 +470,19 @@ class IDLExternalInterface(IDLObjectWith
         return False
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
     def resolve(self, parentScope):
         pass
 
+    def _getDependentObjects(self):
+        return set()
+
 class IDLInterface(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members,
                  isPartial):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert not isPartial or not parent
 
         self.parent = None
@@ -911,16 +946,23 @@ class IDLInterface(IDLObjectWithScope):
         # Now make it look like we were parsed at this new location, since
         # that's the place where the interface is "really" defined
         self.location = location
         assert not self.parent
         self.parent = parent
         # Put the new members at the beginning
         self.members = members + self.members
 
+    def _getDependentObjects(self):
+        deps = set(self.members)
+        deps.union(self.implementedInterfaces)
+        if self.parent:
+            deps.add(self.parent)
+        return deps
+
 class IDLDictionary(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
 
         self.parent = parent
         self._finished = False
@@ -981,16 +1023,21 @@ class IDLDictionary(IDLObjectWithScope):
                                       [member.location, inheritedMember.location])
 
     def validate(self):
         pass
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
+    def _getDependentObjects(self):
+        deps = set(self.members)
+        if (self.parent):
+            deps.add(self.parent)
+        return deps
 
 class IDLEnum(IDLObjectWithIdentifier):
     def __init__(self, location, parentScope, name, values):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
 
         if len(values) != len(set(values)):
             raise WebIDLError("Enum %s has multiple identical strings" % name.name,
@@ -1009,16 +1056,19 @@ class IDLEnum(IDLObjectWithIdentifier):
         pass
 
     def isEnum(self):
         return True
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
+    def _getDependentObjects(self):
+        return set()
+
 class IDLType(IDLObject):
     Tags = enum(
         # The integer types
         'int8',
         'uint8',
         'int16',
         'uint16',
         'int32',
@@ -1298,16 +1348,19 @@ class IDLNullableType(IDLType):
 
     def isDistinguishableFrom(self, other):
         if (other.nullable() or (other.isUnion() and other.hasNullableType) or
             other.isDictionary()):
             # Can't tell which type null should become
             return False
         return self.inner.isDistinguishableFrom(other)
 
+    def _getDependentObjects(self):
+        return self.inner._getDependentObjects()
+
 class IDLSequenceType(IDLType):
     def __init__(self, location, parameterType):
         assert not parameterType.isVoid()
 
         IDLType.__init__(self, location, parameterType.name)
         self.inner = parameterType
         self.builtin = False
 
@@ -1368,16 +1421,19 @@ class IDLSequenceType(IDLType):
 
     def isDistinguishableFrom(self, other):
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isNonCallbackInterface())
 
+    def _getDependentObjects(self):
+        return self.inner._getDependentObjects()
+
 class IDLUnionType(IDLType):
     def __init__(self, location, memberTypes):
         IDLType.__init__(self, location, "")
         self.memberTypes = memberTypes
         self.hasNullableType = False
         self.hasDictionaryType = False
         self.flatMemberTypes = None
         self.builtin = False
@@ -1471,16 +1527,19 @@ class IDLUnionType(IDLType):
             otherTypes = [other]
         # For every type in otherTypes, check that it's distinguishable from
         # every type in our types
         for u in otherTypes:
             if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
                 return False
         return True
 
+    def _getDependentObjects(self):
+        return set(self.memberTypes)
+
 class IDLArrayType(IDLType):
     def __init__(self, location, parameterType):
         assert not parameterType.isVoid()
         if parameterType.isSequence():
             raise WebIDLError("Array type cannot parameterize over a sequence type",
                               [location])
         if parameterType.isDictionary():
             raise WebIDLError("Array type cannot parameterize over a dictionary type",
@@ -1546,16 +1605,19 @@ class IDLArrayType(IDLType):
 
     def isDistinguishableFrom(self, other):
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isNonCallbackInterface())
 
+    def _getDependentObjects(self):
+        return self.inner._getDependentObjects()
+
 class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
     def __init__(self, location, innerType, name):
         IDLType.__init__(self, location, innerType.name)
 
         identifier = IDLUnresolvedIdentifier(location, name)
 
         IDLObjectWithIdentifier.__init__(self, location, None, identifier)
 
@@ -1633,16 +1695,19 @@ class IDLTypedefType(IDLType, IDLObjectW
         return self.inner.tag()
 
     def unroll(self):
         return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return self.inner.isDistinguishableFrom(other)
 
+    def _getDependentObjects(self):
+        return self.inner._getDependentObjects()
+
 class IDLWrapperType(IDLType):
     def __init__(self, location, inner):
         IDLType.__init__(self, location, inner.identifier.name)
         self.inner = inner
         self._identifier = inner.identifier
         self.builtin = False
 
     def __eq__(self, other):
@@ -1736,16 +1801,33 @@ class IDLWrapperType(IDLType):
         if (other.isDictionary() or other.isCallback() or
             other.isSequence() or other.isArray()):
             return self.isNonCallbackInterface()
 
         # Not much else |other| can be
         assert other.isObject()
         return False
 
+    def _getDependentObjects(self):
+        # NB: The codegen for an interface type depends on
+        #  a) That the identifier is in fact an interface (as opposed to
+        #     a dictionary or something else).
+        #  b) The native type of the interface.
+        #  If we depend on the interface object we will also depend on
+        #  anything the interface depends on which is undesirable.  We
+        #  considered implementing a dependency just on the interface type
+        #  file, but then every modification to an interface would cause this
+        #  to be regenerated which is still undesirable.  We decided not to
+        #  depend on anything, reasoning that:
+        #  1) Changing the concrete type of the interface requires modifying
+        #     Bindings.conf, which is still a global dependency.
+        #  2) Changing an interface to a dictionary (or vice versa) with the
+        #     same identifier should be incredibly rare.
+        return set()
+
 class IDLBuiltinType(IDLType):
 
     Types = enum(
         # The integer types
         'byte',
         'octet',
         'short',
         'unsigned_short',
@@ -1901,16 +1983,19 @@ class IDLBuiltinType(IDLType):
                  (self.isArrayBufferView() and not other.isArrayBufferView() and
                   not other.isTypedArray()) or
                  # Typed arrays are distinguishable from everything
                  # except ArrayBufferView and the same type of typed
                  # array
                  (self.isTypedArray() and not other.isArrayBufferView() and not
                   (other.isTypedArray() and other.name == self.name)))))
 
+    def _getDependentObjects(self):
+        return set()
+
 BuiltinTypes = {
       IDLBuiltinType.Types.byte:
           IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
                          IDLBuiltinType.Types.byte),
       IDLBuiltinType.Types.octet:
           IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet",
                          IDLBuiltinType.Types.octet),
       IDLBuiltinType.Types.short:
@@ -2059,16 +2144,19 @@ class IDLValue(IDLObject):
                 raise WebIDLError("'%s' is not a valid default value for enum %s"
                                   % (self.value, type.inner.identifier.name),
                                   [location, type.inner.location])
             return self
         else:
             raise WebIDLError("Cannot coerce type %s to type %s." %
                               (self.type, type), [location])
 
+    def _getDependentObjects(self):
+        return set()
+
 class IDLNullValue(IDLObject):
     def __init__(self, location):
         IDLObject.__init__(self, location)
         self.type = None
         self.value = None
 
     def coerceToType(self, type, location):
         if (not isinstance(type, IDLNullableType) and
@@ -2076,17 +2164,20 @@ class IDLNullValue(IDLObject):
             not type.isDictionary() and
             not type.isAny()):
             raise WebIDLError("Cannot coerce null value to type %s." % type,
                               [location])
 
         nullValue = IDLNullValue(self.location)
         nullValue.type = type
         return nullValue
-        
+
+    def _getDependentObjects(self):
+        return set()
+  
 
 class IDLInterfaceMember(IDLObjectWithIdentifier):
 
     Tags = enum(
         'Const',
         'Attr',
         'Method'
     )
@@ -2157,16 +2248,19 @@ class IDLConst(IDLInterfaceMember):
         coercedValue = self.value.coerceToType(self.type, self.location)
         assert coercedValue
 
         self.value = coercedValue
 
     def validate(self):
         pass
 
+    def _getDependentObjects(self):
+        return set([self.type, self.value])
+
 class IDLAttribute(IDLInterfaceMember):
     def __init__(self, location, identifier, type, readonly, inherit=False,
                  static=False, stringifier=False):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Attr)
 
         assert isinstance(type, IDLType)
         self.type = type
@@ -2302,16 +2396,19 @@ class IDLAttribute(IDLInterfaceMember):
         IDLInterfaceMember.addExtendedAttributes(self, attrs)
 
     def hasLenientThis(self):
         return self.lenientThis
 
     def isUnforgeable(self):
         return self._unforgeable
 
+    def _getDependentObjects(self):
+        return set([self.type])
+
 class IDLArgument(IDLObjectWithIdentifier):
     def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
         IDLObjectWithIdentifier.__init__(self, location, None, identifier)
 
         assert isinstance(type, IDLType)
         self.type = type
 
         self.optional = optional
@@ -2374,16 +2471,22 @@ class IDLArgument(IDLObjectWithIdentifie
 
         # Now do the coercing thing; this needs to happen after the
         # above creation of a default value.
         if self.defaultValue:
             self.defaultValue = self.defaultValue.coerceToType(self.type,
                                                                self.location)
             assert self.defaultValue
 
+    def _getDependentObjects(self):
+        deps = set([self.type])
+        if self.defaultValue:
+            deps.add(self.defaultValue)
+        return deps
+
 class IDLCallbackType(IDLType, IDLObjectWithScope):
     def __init__(self, location, parentScope, identifier, returnType, arguments):
         assert isinstance(returnType, IDLType)
 
         IDLType.__init__(self, location, identifier.name)
 
         self._returnType = returnType
         # Clone the list
@@ -2441,31 +2544,39 @@ class IDLCallbackType(IDLType, IDLObject
         for attr in attrs:
             if attr.identifier() == "TreatNonCallableAsNull":
                 self._treatNonCallableAsNull = True
             else:
                 unhandledAttrs.append(attr)
         if len(unhandledAttrs) != 0:
             IDLType.addExtendedAttributes(self, unhandledAttrs)
 
+    def _getDependentObjects(self):
+        return set([self._returnType] + self._arguments)
+
 class IDLMethodOverload:
     """
     A class that represents a single overload of a WebIDL method.  This is not
     quite the same as an element of the "effective overload set" in the spec,
     because separate IDLMethodOverloads are not created based on arguments being
     optional.  Rather, when multiple methods have the same name, there is an
     IDLMethodOverload for each one, all hanging off an IDLMethod representing
     the full set of overloads.
     """
     def __init__(self, returnType, arguments, location):
         self.returnType = returnType
         # Clone the list of arguments, just in case
         self.arguments = list(arguments)
         self.location = location
 
+    def _getDependentObjects(self):
+        deps = set(self.arguments)
+        deps.add(self.returnType)
+        return deps
+
 class IDLMethod(IDLInterfaceMember, IDLScope):
 
     Special = enum(
         'Getter',
         'Setter',
         'Creator',
         'Deleter',
         'LegacyCaller',
@@ -2803,16 +2914,22 @@ class IDLMethod(IDLInterfaceMember, IDLS
                 raise WebIDLError("[LenientFloat] used on a non-void method",
                                   [attr.location, self.location])
             if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
                 raise WebIDLError("[LenientFloat] used on an operation with no "
                                   "restricted float type arguments",
                                   [attr.location, self.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
+    def _getDependentObjects(self):
+        deps = set()
+        for overload in self._overloads:
+            deps.union(overload._getDependentObjects())
+        return deps
+
 class IDLImplementsStatement(IDLObject):
     def __init__(self, location, implementor, implementee):
         IDLObject.__init__(self, location)
         self.implementor = implementor;
         self.implementee = implementee
 
     def finish(self, scope):
         assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
--- a/dom/bindings/parser/tests/test_nullable_equivalency.py
+++ b/dom/bindings/parser/tests/test_nullable_equivalency.py
@@ -85,17 +85,17 @@ def checkEquivalent(iface, harness):
     #  - names beginning with '_',
     #  - functions which throw when called with no args, and
     #  - class-level non-callables ("static variables").
     #
     # Yes, this is an ugly, fragile hack.  But it finds bugs...
     for attr in dir(type1):
         if attr.startswith('_') or \
            attr in ['nullable', 'builtin', 'filename', 'location',
-                    'inner', 'QName'] or \
+                    'inner', 'QName', 'getDeps'] or \
            (hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
             continue
 
         a1 = getattr(type1, attr)
 
         if callable(a1):
             try:
                 v1 = a1()
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -39,17 +39,16 @@ LOCAL_INCLUDES += \
 # dom/bindings/Makefile.in too.  But note that we include ../Makefile
 # here manually, since $(GLOBAL_DEPS) won't cover it.
 bindinggen_dependencies := \
   ../BindingGen.py \
   ../Bindings.conf \
   ../Configuration.py \
   ../Codegen.py \
   ../parser/WebIDL.py \
-  ../ParserResults.pkl \
   ../Makefile \
   $(GLOBAL_DEPS) \
   $(NULL)
 
 MOCHITEST_FILES := \
   test_bug773326.html \
   test_enums.html \
   test_integers.html \
--- a/dom/bindings/test/test_bug773326.html
+++ b/dom/bindings/test/test_bug773326.html
@@ -1,11 +1,16 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>Test for Bug 773326</title>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <div id=log></div>
 <script>
+if (navigator.platform.indexOf("Linux") == 0) {
+  SimpleTest.expectAssertions(0, 2);
+} else {
+  SimpleTest.expectAssertions(2);
+}
 test(function() {
   new Worker("data:text/javascript,new XMLHttpRequest(42)");
 }, "Should not crash")
 </script>
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -114,48 +114,78 @@ BluetoothOppManagerObserver::Observe(nsI
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return sInstance->HandleShutdown();
   }
 
   MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
+class SendSocketDataTask : public nsRunnable
+{
+public:
+  SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
+    : mStream(aStream)
+    , mSize(aSize)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    sInstance->SendPutRequest(mStream, mSize);
+    sSentFileLength += mSize;
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoArrayPtr<uint8_t> mStream;
+  uint32_t mSize;
+};
+
 class ReadFileTask : public nsRunnable
 {
 public:
   ReadFileTask(nsIInputStream* aInputStream,
                uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     uint32_t numRead;
-    nsAutoArrayPtr<char> buf;
-    buf = new char[mAvailablePacketSize];
+    nsAutoArrayPtr<char> buf(new char[mAvailablePacketSize]);
 
     // function inputstream->Read() only works on non-main thread
-    nsresult rv = mInputStream->Read(buf.get(), mAvailablePacketSize, &numRead);
+    nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead);
     if (NS_FAILED(rv)) {
       // Needs error handling here
+      NS_WARNING("Failed to read from input stream");
       return NS_ERROR_FAILURE;
     }
 
     if (numRead > 0) {
       if (sSentFileLength + numRead >= sFileLength) {
         sWaitingToSendPutFinal = true;
       }
-      sInstance->SendPutRequest((uint8_t*)buf.get(), numRead);
-      sSentFileLength += numRead;
+
+      nsRefPtr<SendSocketDataTask> task =
+        new SendSocketDataTask((uint8_t*)buf.forget(), numRead);
+      if (NS_FAILED(NS_DispatchToMainThread(task))) {
+        NS_WARNING("Failed to dispatch to main thread!");
+        return NS_ERROR_FAILURE;
+      }
     }
 
     return NS_OK;
   };
 
 private:
   nsCOMPtr<nsIInputStream> mInputStream;
   uint32_t mAvailablePacketSize;
@@ -356,16 +386,17 @@ BluetoothOppManager::SendFile(BlobParent
         sFileName.AppendLiteral(".");
         AppendUTF8toUTF16(extension, sFileName);
       }
     }
   }
 
   SendConnectRequest();
   mTransferMode = false;
+  StartFileTransfer();
 
   return true;
 }
 
 bool
 BluetoothOppManager::StopSendingFile()
 {
   mAbortFlag = true;
@@ -792,34 +823,36 @@ BluetoothOppManager::ClientDataHandler(U
   if (mPacketLeftLength > 0) {
     opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
     packetLength = mPacketLeftLength;
   } else {
     opCode = aMessage->mData[0];
     packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
   }
 
-  // Check response code
-  if (mLastCommand == ObexRequestCode::Put &&
-      opCode != ObexResponseCode::Continue) {
-    NS_WARNING("[OPP] Put(0x02) failed");
-    SendDisconnectRequest();
+  // Check response code and send out system message as finished if the reponse
+  // code is somehow incorrect.
+  uint8_t expectedOpCode = ObexResponseCode::Success;
+  if (mLastCommand == ObexRequestCode::Put) {
+    expectedOpCode = ObexResponseCode::Continue;
+  }
+
+  if (opCode != expectedOpCode) {
+    if (mLastCommand == ObexRequestCode::Put ||
+        mLastCommand == ObexRequestCode::Abort ||
+        mLastCommand == ObexRequestCode::PutFinal) {
+      SendDisconnectRequest();
+    }
+    nsAutoCString str;
+    str += "[OPP] 0x";
+    str.AppendInt(mLastCommand, 16);
+    str += " failed";
+    NS_WARNING(str.get());
+    FileTransferComplete();
     return;
-  } else if (mLastCommand == ObexRequestCode::Abort ||
-             mLastCommand == ObexRequestCode::Connect ||
-             mLastCommand == ObexRequestCode::Disconnect ||
-             mLastCommand == ObexRequestCode::PutFinal){
-    if (opCode != ObexResponseCode::Success) {
-      nsAutoCString str;
-      str += "[OPP] 0x";
-      str += mLastCommand;
-      str += " failed";
-      NS_WARNING(str.get());
-      return;
-    }
   }
 
   if (mLastCommand == ObexRequestCode::PutFinal) {
     mSuccessFlag = true;
     FileTransferComplete();
     SendDisconnectRequest();
   } else if (mLastCommand == ObexRequestCode::Abort) {
     SendDisconnectRequest();
@@ -844,17 +877,16 @@ BluetoothOppManager::ClientDataHandler(U
       (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
 
     /*
      * Before sending content, we have to send a header including
      * information such as file name, file length and content type.
      */
     if (ExtractBlobHeaders()) {
       sInstance->SendPutHeaderRequest(sFileName, sFileLength);
-      StartFileTransfer();
     }
   } else if (mLastCommand == ObexRequestCode::Put) {
 
     // Send PutFinal packet when we get response
     if (sWaitingToSendPutFinal) {
       SendPutFinalRequest();
       return;
     }
--- a/dom/bluetooth/BluetoothScoManager.cpp
+++ b/dom/bluetooth/BluetoothScoManager.cpp
@@ -117,17 +117,17 @@ BluetoothScoManager::BluetoothScoManager
 }
 
 bool
 BluetoothScoManager::Init()
 {
   mSocketStatus = GetConnectionStatus();
 
   sScoObserver = new BluetoothScoManagerObserver();
-  if (sScoObserver->Init()) {
+  if (!sScoObserver->Init()) {
     NS_WARNING("Cannot set up SCO observers!");
   }
   return true;
 }
 
 BluetoothScoManager::~BluetoothScoManager()
 {
   Cleanup();
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -2461,21 +2461,19 @@ BluetoothDBusService::Connect(const nsAS
     }
   } else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
     BluetoothOppManager* opp = BluetoothOppManager::Get();
     if (!opp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
                       aRunnable)) {
       errorStr.AssignLiteral("BluetoothOppManager has connected/is connecting!");
       DispatchBluetoothReply(aRunnable, v, errorStr);
     }
+  } else {
+    NS_WARNING("Unknown Profile");
   }
-
-#ifdef DEBUG
-  NS_WARNING("Unknown Profile");
-#endif
 }
 
 void
 BluetoothDBusService::Disconnect(const uint16_t aProfileId,
                                  BluetoothReplyRunnable* aRunnable)
 {
   if (aProfileId == BluetoothServiceClass::HANDSFREE ||
       aProfileId == BluetoothServiceClass::HEADSET) {
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -8,47 +8,48 @@
 //   #define CreateEvent CreateEventW
 // That messes up our call to nsEventDispatcher::CreateEvent below.
 
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
 
 #include "BrowserElementParent.h"
-#include "nsHTMLIFrameElement.h"
+#include "mozilla/dom/HTMLIFrameElement.h"
 #include "nsOpenWindowEventDetail.h"
 #include "nsEventDispatcher.h"
 #include "nsIDOMCustomEvent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsVariant.h"
 #include "nsAsyncScrollEventDetail.h"
 
 using mozilla::dom::Element;
+using mozilla::dom::HTMLIFrameElement;
 using mozilla::dom::TabParent;
 
 namespace {
 
 /**
  * Create an <iframe mozbrowser> owned by the same document as
  * aOpenerFrameElement.
  */
-already_AddRefed<nsHTMLIFrameElement>
+already_AddRefed<HTMLIFrameElement>
 CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
 {
   nsNodeInfoManager *nodeInfoManager =
     aOpenerFrameElement->OwnerDoc()->NodeInfoManager();
 
   nsCOMPtr<nsINodeInfo> nodeInfo =
     nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
                                  /* aPrefix = */ nullptr,
                                  kNameSpaceID_XHTML,
                                  nsIDOMNode::ELEMENT_NODE);
 
-  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
-    static_cast<nsHTMLIFrameElement*>(
+  nsRefPtr<HTMLIFrameElement> popupFrameElement =
+    static_cast<HTMLIFrameElement*>(
       NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
 
   popupFrameElement->SetMozbrowser(true);
 
   // Copy the opener frame's mozapp attribute to the popup frame.
   if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp)) {
     nsAutoString mozapp;
     aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, mozapp);
@@ -146,17 +147,17 @@ BrowserElementParent::OpenWindowOOP(TabP
                                     const nsAString& aURL,
                                     const nsAString& aName,
                                     const nsAString& aFeatures)
 {
   // Create an iframe owned by the same document which owns openerFrameElement.
   nsCOMPtr<Element> openerFrameElement =
     do_QueryInterface(aOpenerTabParent->GetOwnerElement());
   NS_ENSURE_TRUE(openerFrameElement, false);
-  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
+  nsRefPtr<HTMLIFrameElement> popupFrameElement =
     CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
 
   // Normally an <iframe> element will try to create a frameLoader when the
   // page touches iframe.contentWindow or sets iframe.src.
   //
   // But in our case, we want to delay the creation of the frameLoader until
   // we've verified that the popup has gone through successfully.  If the popup
   // is "blocked" by the embedder, we don't want to load the popup's url.
@@ -204,17 +205,17 @@ BrowserElementParent::OpenWindowInProces
 
   nsCOMPtr<nsIDOMElement> openerFrameDOMElement;
   topWindow->GetFrameElement(getter_AddRefs(openerFrameDOMElement));
   NS_ENSURE_TRUE(openerFrameDOMElement, false);
 
   nsCOMPtr<Element> openerFrameElement =
     do_QueryInterface(openerFrameDOMElement);
 
-  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
+  nsRefPtr<HTMLIFrameElement> popupFrameElement =
     CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   NS_ENSURE_TRUE(popupFrameElement, false);
 
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
   bool dispatchSucceeded =
--- a/dom/browser-element/mochitest/browserElement_AppFramePermission.js
+++ b/dom/browser-element/mochitest/browserElement_AppFramePermission.js
@@ -3,17 +3,17 @@
 
 // Bug 777384 - Test mozapp permission.
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 function makeAllAppsLaunchable() {
   var Webapps = {};
-  SpecialPowers.wrap(Components).utils.import("resource://gre/modules/Webapps.jsm", Webapps);
+  SpecialPowers.Cu.import("resource://gre/modules/Webapps.jsm", Webapps);
   var appRegistry = SpecialPowers.wrap(Webapps.DOMApplicationRegistry);
 
   var originalValue = appRegistry.allAppsLaunchable;
   appRegistry.allAppsLaunchable = true;
 
   // Clean up after ourselves once tests are done so the test page is unloaded.
   window.addEventListener("unload", function restoreAllAppsLaunchable(event) {
     if (event.target == window.document) {
--- a/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html
+++ b/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html
@@ -16,17 +16,17 @@ function handlePrompt(e) {
   }
   else if (numPrompts == 3) {
     if (e.detail.message != 'child2:hidden') {
       alert("parent:fail Didn't get expected 'child2:hidden'.");
     }
 
     alert('parent:ready');
   }
-  else if (numPrompts > 3) {
+  else if (numPrompts == 4 || numPrompts == 5) {
     alert(e.detail.message);
   }
 }
 
 var iframe1 = document.createElement('iframe');
 iframe1.mozbrowser = true;
 iframe1.addEventListener('mozbrowsershowmodalprompt', handlePrompt);
 
--- a/dom/browser-element/mochitest/test_browserElement_inproc_PurgeHistory.html
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_PurgeHistory.html
@@ -2,12 +2,15 @@
 <html>
 <head>
   <title>Test of browser element.</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="browserElementTestHelpers.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
+<script type="application/javascript;version=1.7">
+SimpleTest.expectAssertions(1);
+</script>
 <script type="application/javascript;version=1.7" src="browserElement_PurgeHistory.js">
 </script>
 </body>
 </html>
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -385,16 +385,24 @@ ContactManager.prototype = {
   _convertContacts: function(aContacts) {
     let contacts = [];
     for (let i in aContacts) {
       contacts.push(this._convertContact(aContacts[i]));
     }
     return contacts;
   },
 
+  _fireSuccessOrDone: function(aCursor, aResult) {
+    if (aResult == null) {
+      Services.DOMRequest.fireDone(aCursor);
+    } else {
+      Services.DOMRequest.fireSuccess(aCursor, aResult);
+    }
+  },
+
   receiveMessage: function(aMessage) {
     if (DEBUG) debug("receiveMessage: " + aMessage.name);
     let msg = aMessage.json;
     let contacts = msg.contacts;
 
     let req;
     switch (aMessage.name) {
       case "Contacts:Find:Return:OK":
@@ -402,22 +410,25 @@ ContactManager.prototype = {
         if (req) {
           let result = this._convertContacts(contacts);
           Services.DOMRequest.fireSuccess(req.request, result);
         } else {
           if (DEBUG) debug("no request stored!" + msg.requestID);
         }
         break;
       case "Contacts:GetAll:Next":
-        let cursor = this._cursorData[msg.cursorId];
+        let data = this._cursorData[msg.cursorId];
         let contact = msg.contact ? this._convertContact(msg.contact) : null;
-        if (contact == null) {
-          Services.DOMRequest.fireDone(cursor);
+        if (data.waitingForNext) {
+          if (DEBUG) debug("cursor waiting for contact, sending");
+          data.waitingForNext = false;
+          this._fireSuccessOrDone(data.cursor, contact);
         } else {
-          Services.DOMRequest.fireSuccess(cursor, contact);
+          if (DEBUG) debug("cursor not waiting, saving");
+          data.cachedContacts.push(contact);
         }
         break;
       case "Contacts:GetSimContacts:Return:OK":
         req = this.getRequest(msg.requestID);
         if (req) {
           let result = contacts.map(function(c) {
             let contact = new Contact();
             let prop = {name: [c.alphaId], tel: [ { value: c.number } ]};
@@ -586,40 +597,49 @@ ContactManager.prototype = {
       cpmm.sendAsyncMessage("Contacts:Find", {requestID: this.getRequestId({request: request, reason: "find"}), options: options});
     }.bind(this)
     this.askPermission("find", request, allowCallback);
     return request;
   },
 
   createCursor: function CM_createCursor(aRequest) {
     let id = this._getRandomId();
-    let cursor = Services.DOMRequest.createCursor(this._window, function() {
-      this.handleContinue(id);
-    }.bind(this));
+    let data = {
+      cursor: Services.DOMRequest.createCursor(this._window, function() {
+        this.handleContinue(id);
+      }.bind(this)),
+      cachedContacts: [],
+      waitingForNext: true,
+    };
     if (DEBUG) debug("saved cursor id: " + id);
-    this._cursorData[id] = cursor;
-    return [id, cursor];
+    this._cursorData[id] = data;
+    return [id, data.cursor];
   },
 
   getAll: function CM_getAll(aOptions) {
     if (DEBUG) debug("getAll: " + JSON.stringify(aOptions));
     let [cursorId, cursor] = this.createCursor();
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetAll", {
         cursorId: cursorId, findOptions: aOptions});
     }.bind(this);
     this.askPermission("find", cursor, allowCallback);
     return cursor;
   },
 
   handleContinue: function CM_handleContinue(aCursorId) {
     if (DEBUG) debug("handleContinue: " + aCursorId);
-    cpmm.sendAsyncMessage("Contacts:GetAll:Continue", {
-      cursorId: aCursorId
-    });
+    let data = this._cursorData[aCursorId];
+    if (data.cachedContacts.length > 0) {
+      if (DEBUG) debug("contact in cache");
+      this._fireSuccessOrDone(data.cursor, data.cachedContacts.shift());
+    } else {
+      if (DEBUG) debug("waiting for contact");
+      data.waitingForNext = true;
+    }
   },
 
   remove: function removeContact(aRecord) {
     let request;
     request = this.createRequest();
     let options = { id: aRecord.id };
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contact:Remove", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -25,18 +25,16 @@ const SAVED_GETALL_STORE_NAME = "getallc
 this.ContactDB = function ContactDB(aGlobal) {
   if (DEBUG) debug("Constructor");
   this._global = aGlobal;
 }
 
 ContactDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
-  cursorData: {},
-
   upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     if (DEBUG) debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!");
     let db = aDb;
     let objectStore;
     for (let currVersion = aOldVersion; currVersion < aNewVersion; currVersion++) {
       if (currVersion == 0) {
         /**
          * Create the initial database schema.
@@ -467,127 +465,98 @@ ContactDB.prototype = {
 
   clear: function clear(aSuccessCb, aErrorCb) {
     this.newTxn("readwrite", STORE_NAME, function (txn, store) {
       if (DEBUG) debug("Going to clear all!");
       store.clear();
     }, aSuccessCb, aErrorCb);
   },
 
-  getObjectById: function CDB_getObjectById(aStore, aObjectId, aCallback) {
-    if (DEBUG) debug("getObjectById: " + aStore + ":" + aObjectId);
-    this.newTxn("readonly", aStore, function (txn, store) {
-      let req = store.get(aObjectId);
-      req.onsuccess = function (event) {
-        aCallback(event.target.result);
-      };
-      req.onerror = function (event) {
-        aCallback(null);
-      };
-    });
-  },
-
-  getCacheForQuery: function CDB_getCacheForQuery(aQuery, aCursorId, aSuccessCb) {
-    if (DEBUG) debug("getCacheForQuery");
-    // Here we try to get the cached results for query `aQuery'. If they don't
-    // exist, it means the cache was invalidated and needs to be recreated, so
-    // we do that. Otherwise, we just return the existing cache.
-    this.getObjectById(SAVED_GETALL_STORE_NAME, aQuery, function (aCache) {
-      if (!aCache) {
-        if (DEBUG) debug("creating cache for query " + aQuery);
-        this.createCacheForQuery(aQuery, aCursorId, aSuccessCb);
-      } else {
-        if (DEBUG) debug("cache exists");
-        if (!this.cursorData[aCursorId]) {
-          this.cursorData[aCursorId] = aCache;
-        }
-        aSuccessCb(aCache);
-      }
-    }.bind(this));
-  },
-
-  setCacheForQuery: function CDB_setCacheForQuery(aQuery, aCache, aCallback) {
-    this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function (txn, store) {
-      let req = store.put(aCache, aQuery);
-      if (!aCallback) {
-        return;
-      }
-      req.onsuccess = function () {
-        aCallback(true);
-      };
-      req.onerror = function () {
-        aCallback(false);
-      };
-    });
-  },
-
-  createCacheForQuery: function CDB_createCacheForQuery(aQuery, aCursorId, aSuccessCb, aFailureCb) {
+  createCacheForQuery: function CDB_createCacheForQuery(aQuery, aSuccessCb, aFailureCb) {
     this.find(function (aContacts) {
       if (aContacts) {
         let contactsArray = [];
         for (let i in aContacts) {
-          contactsArray.push(aContacts[i].id);
+          contactsArray.push(aContacts[i]);
         }
 
-        this.setCacheForQuery(aQuery, contactsArray);
-        this.cursorData[aCursorId] = contactsArray;
-        aSuccessCb(contactsArray);
+        // save contact ids in cache
+        this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) {
+          store.put(contactsArray.map(function(el) el.id), aQuery);
+        });
+
+        // send full contacts
+        aSuccessCb(contactsArray, true);
       } else {
-        aSuccessCb(null);
+        aSuccessCb([], true);
       }
     }.bind(this),
     function (aErrorMsg) { aFailureCb(aErrorMsg); },
     JSON.parse(aQuery));
   },
 
-  getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions, aCursorId) {
-    // Recreate the cache for this query if needed
+  getCacheForQuery: function CDB_getCacheForQuery(aQuery, aSuccessCb) {
+    if (DEBUG) debug("getCacheForQuery");
+    // Here we try to get the cached results for query `aQuery'. If they don't
+    // exist, it means the cache was invalidated and needs to be recreated, so
+    // we do that. Otherwise, we just return the existing cache.
+    this.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) {
+      let req = store.get(aQuery);
+      req.onsuccess = function(e) {
+        if (e.target.result) {
+          if (DEBUG) debug("cache exists");
+          debug("sending: " + JSON.stringify(e.target.result));
+          aSuccessCb(e.target.result, false);
+        } else {
+          if (DEBUG) debug("creating cache for query " + aQuery);
+          this.createCacheForQuery(aQuery, aSuccessCb);
+        }
+      }.bind(this);
+      req.onerror = function() {
+
+      };
+    }.bind(this));
+  },
+
+  getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions) {
     let optionStr = JSON.stringify(aOptions);
-    this.getCacheForQuery(optionStr, aCursorId, function (aCachedResults) {
+    this.getCacheForQuery(optionStr, function(aCachedResults, aFullContacts) {
+      // aFullContacts is true if the cache didn't exist and had to be created.
+      // In that case, we receive the full contacts since we already have them
+      // in memory to create the cache anyway. This allows us to avoid accessing
+      // the main object store again.
       if (aCachedResults && aCachedResults.length > 0) {
         if (DEBUG) debug("query returned at least one contact");
-        this.getObjectById(STORE_NAME, aCachedResults[0], function (aContact) {
-          this.cursorData[aCursorId].shift();
-          aSuccessCb(aContact);
-        }.bind(this));
+        if (aFullContacts) {
+          if (DEBUG) debug("full contacts: " + aCachedResults.length);
+          for (let i = 0; i < aCachedResults.length; ++i) {
+            aSuccessCb(aCachedResults[i]);
+          }
+          aSuccessCb(null);
+        } else {
+          let count = 0;
+          this.newTxn("readonly", STORE_NAME, function(txn, store) {
+            for (let i = 0; i < aCachedResults.length; ++i) {
+              store.get(aCachedResults[i]).onsuccess = function(e) {
+                aSuccessCb(e.target.result);
+                count++;
+                if (count == aCachedResults.length) {
+                  aSuccessCb(null);
+                }
+              };
+            }
+          });
+        }
       } else { // no contacts
         if (DEBUG) debug("query returned no contacts");
         aSuccessCb(null);
       }
     }.bind(this));
   },
 
-  getNext: function CDB_getNext(aSuccessCb, aFailureCb, aCursorId) {
-    if (DEBUG) debug("ContactDB:getNext: " + aCursorId);
-    let aCachedResults = this.cursorData[aCursorId];
-    if (DEBUG) debug("got transient cache");
-    if (aCachedResults.length > 0) {
-      this.getObjectById(STORE_NAME, aCachedResults[0], function(aContact) {
-        this.cursorData[aCursorId].shift();
-        if (aContact) {
-          aSuccessCb(aContact);
-        } else {
-          // If the contact ID in cache is invalid, it was removed recently and
-          // the cache hasn't been updated to reflect the change, so we skip it.
-          if (DEBUG) debug("invalid contact in cache: " + aCachedResults[0]);
-          return this.getNext(aSuccessCb, aFailureCb, aCursorId);
-        }
-      }.bind(this));
-    } else { // last contact
-      delete this.cursorData[aCursorId];
-      aSuccessCb(null);
-    }
-  },
-
-  releaseCursors: function CDB_releaseCursors(aCursors) {
-    for (let i of aCursors) {
-      delete this.cursorData[i];
-    }
-  },
-
   /*
    * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName.
    * If 2 entries have the same sortyBy field or no sortBy field is present, we continue
    * sorting with the other sortyBy field.
    */
   sortResults: function CDB_sortResults(aResults, aFindOptions) {
     if (!aFindOptions)
       return;
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -32,22 +32,19 @@ XPCOMUtils.defineLazyGetter(this, "mRIL"
     };
   }
   return telephony.getService(Ci.nsIRadioInterfaceLayer);
 });
 
 let myGlobal = this;
 
 this.DOMContactManager = {
-  // maps children to their live cursors so we can cleanup on shutdown/crash
-  _liveCursors: {},
-
   init: function() {
     if (DEBUG) debug("Init");
-    this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:Continue", "Contacts:Clear", "Contact:Save",
+    this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:Clear", "Contact:Save",
                       "Contact:Remove", "Contacts:GetSimContacts",
                       "Contacts:RegisterForMessages", "child-process-shutdown"];
     this._children = [];
     this._messages.forEach(function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
     var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
@@ -114,33 +111,16 @@ this.DOMContactManager = {
           return null;
         }
         this._db.getAll(
           function(aContact) {
             mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contact: aContact});
           },
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
           msg.findOptions, msg.cursorId);
-        if (Array.isArray(this._liveCursors[mm])) {
-          this._liveCursors[mm].push(msg.cursorId);
-        } else {
-          this._liveCursors[mm] = [msg.cursorId];
-        }
-        break;
-      case "Contacts:GetAll:Continue":
-        this._db.getNext(
-          function(aContact) {
-            if (aContact == null) { // last contact, release the cursor
-              let cursors = this._liveCursors[mm];
-              cursors.splice(cursors.indexOf(msg.cursorId), 1);
-            }
-            mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contact: aContact});
-          }.bind(this),
-          function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
-          msg.cursorId);
         break;
       case "Contact:Save":
         if (msg.options.reason === "create") {
           if (!this.assertPermission(aMessage, "contacts-create")) {
             return null;
           }
         } else {
           if (!this.assertPermission(aMessage, "contacts-write")) {
@@ -205,20 +185,16 @@ this.DOMContactManager = {
         }
         if (DEBUG) debug("Register!");
         if (this._children.indexOf(mm) == -1) {
           this._children.push(mm);
         }
         break;
       case "child-process-shutdown":
         if (DEBUG) debug("Unregister");
-        if (this._liveCursors[mm]) {
-          this._db.releaseCursors(this._liveCursors[mm]);
-          delete this._liveCursors[mm];
-        }
         let index = this._children.indexOf(mm);
         if (index != -1) {
           if (DEBUG) debug("Unregister index: " + index);
           this._children.splice(index, 1);
         }
         break;
       default:
         if (DEBUG) debug("WRONG MESSAGE NAME: " + aMessage.name);
--- a/dom/contacts/tests/test_contacts_blobs.html
+++ b/dom/contacts/tests/test_contacts_blobs.html
@@ -49,17 +49,17 @@ function getRandomView(size)
  for (var i = 0; i < size; i++) {
    view[i] = parseInt(Math.random() * 255)
  }
  return view;
 }
 
 function getBlob(type, view)
 {
-  return utils.getBlob([view], {type: type});
+  return SpecialPowers.unwrap(utils.getBlob([view], {type: type}));
 }
 
 function getRandomBlob(size)
 {
   return getBlob("binary/random", getRandomView(size));
 }
 
 function compareBuffers(buffer1, buffer2)
--- a/dom/contacts/tests/test_contacts_getall.html
+++ b/dom/contacts/tests/test_contacts_getall.html
@@ -261,17 +261,17 @@ let steps = [
     };
     req.onerror = onFailure;
   },
 
   clearDatabase,
   add20Contacts,
 
   function() {
-    ok(true, "Test cache invalidation between getAll and getNext");
+    ok(true, "Test cache consistency when deleting contact during getAll");
     req = mozContacts.find({});
     req.onsuccess = function(e) {
       let lastContact = e.target.result[e.target.result.length-1];
       req = mozContacts.getAll({});
       let count = 0;
       let firstResult = true;
       req.onsuccess = function(event) {
         ok(true, "on success");
@@ -285,30 +285,29 @@ let steps = [
             req.continue();
           };
         } else {
           if (req.result) {
             ok(true, "result is valid");
             count++;
             req.continue();
           } else {
-            is(count, 19, "19 contacts returned");
-            ok(true, "last contact");
+            is(count, 20, "last contact - 20 contacts returned");
             next();
           }
         }
       };
     };
   },
 
   clearDatabase,
   add20Contacts,
 
   function() {
-    ok(true, "Delete the currect contact while iterating");
+    ok(true, "Delete the current contact while iterating");
     req = mozContacts.getAll({});
     let count = 0;
     let previousId = null;
     req.onsuccess = function() {
       if (req.result) {
         ok(true, "on success");
         if (previousId) {
           isnot(previousId, req.result.id, "different contacts returned");
--- a/dom/encoding/test/test_TextDecoder.html
+++ b/dom/encoding/test/test_TextDecoder.html
@@ -8,16 +8,18 @@
   <script type="text/javascript" src="test_TextDecoder.js"></script>
   <script type="text/javascript" src="test_BOMEncoding.js"></script>
   <script type="text/javascript" src="worker_helper.js"></script>
 </head>
 <body>
 <div id="log"></div>
 <script>
 
+SimpleTest.expectAssertions(0, 2);
+
 setup({explicit_done: true});
 runTest();
 
 function runTest()
 {
   runTextDecoderOptions();
   runTextDecoderBOMEnoding();
 }
--- a/dom/encoding/test/test_TextEncoder.html
+++ b/dom/encoding/test/test_TextEncoder.html
@@ -7,16 +7,22 @@
   <script type="text/javascript" src="/resources/testharnessreport.js"></script>
   <script type="text/javascript" src="test_TextEncoder.js"></script>
   <script type="text/javascript" src="worker_helper.js"></script>
 </head>
 <body>
 <div id="log"></div>
 <script>
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 2);
+} else {
+  SimpleTest.expectAssertions(2);
+}
+
 setup({explicit_done: true});
 runTest();
 
 function runTest()
 {
   runTextEncoderTests();
 }
 
--- a/dom/file/test/helpers.js
+++ b/dom/file/test/helpers.js
@@ -174,20 +174,20 @@ function compareBuffers(buffer1, buffer2
       return false;
     }
   }
   return true;
 }
 
 function getBlob(type, buffer)
 {
-  return utils.getBlob([buffer], {type: type});
+  return SpecialPowers.unwrap(utils.getBlob([buffer], {type: type}));
 }
 
 function getRandomBlob(size)
 {
   return getBlob("binary/random", getRandomBuffer(size));
 }
 
 function getFileId(blob)
 {
-  return utils.getFileId(blob);
+  return SpecialPowers.unwrap(utils.getFileId(blob));
 }
--- a/dom/file/test/test_archivereader_nonUnicode.html
+++ b/dom/file/test/test_archivereader_nonUnicode.html
@@ -6,18 +6,18 @@
 <head>
   <title>Archive Reader Non-Unicode Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function createNonUnicodeData() {
-    const Cc = SpecialPowers.wrap(Components).classes;
-    const Ci = SpecialPowers.wrap(Components).interfaces;
+    const Cc = SpecialPowers.Cc;
+    const Ci = SpecialPowers.Ci;
 
     var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
     var testFile = dirSvc.get("ProfD", Ci.nsIFile);
     testFile.append("fileArchiveReader_nonUnicode.zip");
     var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
     outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
                    0666, 0);
 
--- a/dom/icc/interfaces/nsIDOMIccManager.idl
+++ b/dom/icc/interfaces/nsIDOMIccManager.idl
@@ -1,16 +1,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 "nsIDOMEventTarget.idl"
 #include "SimToolKit.idl"
 
 interface nsIDOMEventListener;
+interface nsIDOMDOMRequest;
 
 [scriptable, builtinclass, uuid(9d898c66-3485-4cd5-ab8d-92ef2988887b)]
 interface nsIDOMMozIccManager : nsIDOMEventTarget
 {
   /**
    * STK Menu Presentation types.
    */
   const unsigned short STK_MENU_TYPE_NOT_SPECIFIED      = 0x00;
@@ -267,9 +268,51 @@ interface nsIDOMMozIccManager : nsIDOMEv
    */
   [implicit_jscontext] attribute jsval onstkcommand;
 
   /**
    * 'stksessionend' event is notified whenever STK Session is terminated by
    * ICC.
    */
   [implicit_jscontext] attribute jsval onstksessionend;
+
+  // UICC Secure Element Interfaces
+
+  /**
+   * A secure element is a smart card chip that can hold
+   * several different applications with the necessary security.
+   * The most known secure element is the Universal Integrated Circuit Card (UICC)
+   */
+
+  /**
+   * Send request to open a logical channel defined by its
+   * application identifier (AID)
+   *
+   * @param aid
+   *        The Application Identifier of the Applet to be selected on this channel
+   * return value : An instance of Channel (channelID) if available or null.
+   */
+  nsIDOMDOMRequest iccOpenChannel(in DOMString aid);
+
+  /**
+   * Interface, used to communicate with an applet through the
+   * Application Data Protocol Units (APDUs) and is
+   * used for all data that is exchanged between the UICC card and the terminal (ME).
+   *
+   * @param channel
+   *        The Application Identifier of the Applet to which APDU is directed
+   * @param apdu
+   *        Application Protocol Data Unit
+   * return value : Response APDU
+   */
+  nsIDOMDOMRequest iccExchangeAPDU(in long channel, in jsval apdu);
+
+  /**
+   * Send request to close the selected logical channel identified by its
+   * application identifier (AID)
+   *
+   * @param aid
+   *        The Application Identifier of the Applet , to be closed
+   */
+  nsIDOMDOMRequest iccCloseChannel(in long channel);
+
+  // End of UICC Secure Element Interfaces
 };
--- a/dom/icc/src/IccManager.cpp
+++ b/dom/icc/src/IccManager.cpp
@@ -152,14 +152,45 @@ IccManager::SendStkEventDownload(const J
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
   mProvider->SendStkEventDownload(GetOwner(), aEvent);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+IccManager::IccOpenChannel(const nsAString& aAid, nsIDOMDOMRequest** aRequest)
+{
+  if (!mProvider) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return mProvider->IccOpenChannel(GetOwner(), aAid, aRequest);
+}
+
+NS_IMETHODIMP
+IccManager::IccExchangeAPDU(int32_t aChannel, const jsval& aApdu, nsIDOMDOMRequest** aRequest)
+{
+  if (!mProvider) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return mProvider->IccExchangeAPDU(GetOwner(), aChannel, aApdu, aRequest);
+}
+
+NS_IMETHODIMP
+IccManager::IccCloseChannel(int32_t aChannel, nsIDOMDOMRequest** aRequest)
+{
+  if (!mProvider) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return mProvider->IccCloseChannel(GetOwner(), aChannel, aRequest);
+}
+
+
 NS_IMPL_EVENT_HANDLER(IccManager, stkcommand)
 NS_IMPL_EVENT_HANDLER(IccManager, stksessionend)
 
 } // namespace icc
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/test/file.js
+++ b/dom/indexedDB/test/file.js
@@ -44,22 +44,22 @@ function compareBuffers(buffer1, buffer2
       return false;
     }
   }
   return true;
 }
 
 function getBlob(type, view)
 {
-  return utils.getBlob([view], {type: type});
+  return SpecialPowers.unwrap(utils.getBlob([view], {type: type}));
 }
 
 function getFile(name, type, view)
 {
-  return utils.getFile(name, [view], {type: type});
+  return SpecialPowers.unwrap(utils.getFile(name, [view], {type: type}));
 }
 
 function getRandomBlob(size)
 {
   return getBlob("binary/random", getRandomView(size));
 }
 
 function getRandomFile(name, size)
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -5,17 +5,17 @@
 
 var testGenerator = testSteps();
 var archiveReaderEnabled = false;
 
 // The test js is shared between xpcshell (which has no SpecialPowers object)
 // and content mochitests (where the |Components| object is accessible only as
 // SpecialPowers.Components). Expose Components if necessary here to make things
 // work everywhere.
-if (typeof Components === 'undefined')
+if (typeof Components === 'undefined' && typeof SpecialPowers === 'object')
   Components = SpecialPowers.Components;
 
 function executeSoon(aFun)
 {
   let comp = SpecialPowers.wrap(Components);
 
   let thread = comp.classes["@mozilla.org/thread-manager;1"]
                    .getService(comp.interfaces.nsIThreadManager)
--- a/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl
@@ -11,17 +11,17 @@
  *
  * This interface is trying to follow the DOM Level 2 HTML specification:
  * http://www.w3.org/TR/DOM-Level-2-HTML/
  *
  * with changes from the work-in-progress WHATWG HTML specification:
  * http://www.whatwg.org/specs/web-apps/current-work/
  */
 
-[scriptable, uuid(a7bd1e34-3969-47ae-8c1d-2970132ba925)]
+[scriptable, uuid(39298082-9657-48be-b119-4dc9c84ce685)]
 interface nsIDOMHTMLIFrameElement : nsIDOMHTMLElement
 {
            attribute DOMString        align;
            attribute DOMString        frameBorder;
            attribute DOMString        height;
            attribute DOMString        longDesc;
            attribute DOMString        marginHeight;
            attribute DOMString        marginWidth;
@@ -29,14 +29,11 @@ interface nsIDOMHTMLIFrameElement : nsID
            attribute DOMString        scrolling;
            attribute DOMString        src;
            attribute DOMString        width;
   // Introduced in DOM Level 2:
   readonly attribute nsIDOMDocument   contentDocument;
   readonly attribute nsIDOMWindow     contentWindow;
 
            attribute DOMString        sandbox;
-  // Mozilla extensions
-  // iframe elements require the allowfullscreen attribute to be present
-  // if they're to allow content in the sub document to go into DOM full-screen
-  // mode. See https://wiki.mozilla.org/index.php?title=Gecko:FullScreenAPI
-           attribute boolean          allowfullscreen;
+
+           attribute boolean          allowFullscreen;
 };
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -79,16 +79,17 @@ FullScreenDeniedMovedDocument=Request fo
 FullScreenDeniedLostWindow=Request for full-screen was denied because we no longer have a window.
 FullScreenDeniedSubDocFullScreen=Request for full-screen was denied because a subdocument of the document requesting full-screen is already full-screen.
 FullScreenDeniedNotDescendant=Request for full-screen was denied because requesting element is not a descendant of the current full-screen element.
 FullScreenDeniedNotFocusedTab=Request for full-screen was denied because requesting element is not in the currently focused tab.
 RemovedFullScreenElement=Exited full-screen because full-screen element was removed from document.
 FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused.
 HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.
 HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
+MultipartXHRWarning=Support for multipart responses in XMLHttpRequest is going to be removed in an upcoming version. Please migrate to chunked responses or to Web Sockets.
 InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
 ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied.
 ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.
 WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context.
 TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.
 JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
 # LOCALIZATION NOTE: Do not translate decodeAudioData.
 MediaDecodeAudioDataUnknownContentType=The buffer passed to decodeAudioData contains an unknown content type.
--- a/dom/locales/en-US/chrome/mathml/mathml.properties
+++ b/dom/locales/en-US/chrome/mathml/mathml.properties
@@ -1,15 +1,16 @@
 # 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/.
 
 ChildCountIncorrect=Invalid markup: Incorrect number of children for <%1$S/> tag.
 DuplicateMprescripts=Invalid markup: More than one <mprescripts/> in <mmultiscripts/>.
-NoSubSup=Invalid markup: Expected at least one subscript/superscript pair in <mmultiscripts/>.  Found none.
+# LOCALIZATION NOTE:  The first child of <mmultiscript/> is the base, that is the element to which scripts are attached.
+NoBase=Invalid markup: Expected exactly one Base element in <mmultiscripts/>.  Found none.
 SubSupMismatch=Invalid markup: Incomplete subscript/superscript pair in <mmultiscripts/>.
 
 # LOCALIZATION NOTE:  When localizing the single quotes ('), follow the conventions in css.properties for your target locale.
 AttributeParsingError=Error in parsing the value '%1$S' for '%2$S' attribute of <%3$S/>.  Attribute ignored.
 AttributeParsingErrorNoTag=Error in parsing the value '%1$S' for '%2$S' attribute.  Attribute ignored.
 LengthParsingError=Error in parsing MathML attribute value '%1$S' as length.  Attribute ignored.
 DeprecatedSupersededBy='%1$S' is deprecated in MathML 3, superseded by '%2$S'.
 UnitlessValuesAreDeprecated=Unitless values are deprecated in MathML 3.
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -907,17 +907,17 @@ MediaManager::GetUserMedia(bool aPrivile
       nsCOMPtr<nsIPopupWindowManager> pm =
         do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
       if (!pm) {
         return NS_OK;
       }
       uint32_t permission;
       nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
       pm->TestPermission(doc->NodePrincipal(), &permission);
-      if ((permission == nsIPopupWindowManager::DENY_POPUP)) {
+      if (permission == nsIPopupWindowManager::DENY_POPUP) {
         nsCOMPtr<nsIDOMDocument> domDoc = aWindow->GetExtantDocument();
         nsGlobalWindow::FirePopupBlockedEvent(
           domDoc, aWindow, nullptr, EmptyString(), EmptyString()
         );
         return NS_OK;
       }
     }
   }
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -15,16 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Audio Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <audio id="testAudio"></audio>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an audio LocalMediaStream on an audio HTMLMediaElement.
    */
   runTest(function () {
     var testAudio = document.getElementById('testAudio');
 
     getUserMedia({audio: true}, function (aStream) {
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -15,16 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <video id="testVideo"></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an video LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
     var testVideo = document.getElementById('testVideo');
 
     getUserMedia({video: true}, function (aStream) {
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -15,16 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video & Audio Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <video id="testVideoAudio"></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
     var testVideoAudio = document.getElementById('testVideoAudio');
 
     getUserMedia({video: true, audio: true}, function (aStream) {
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -14,16 +14,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795367">Test mozGetUserMedia Exceptions</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 /**
   These tests verify that the appropriate exception is thrown when incorrect
   values are provided to the immediate mozGetUserMedia call.
 */
 var exceptionTests = [
   // Each test here verifies that a caller is required to have all
   // three arguments in order to call mozGetUserMedia
   { params: undefined,
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
@@ -15,16 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Audio Stream</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <video id="testVideo"></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 2);
+}
+
   /**
    * Run a test to verify that we can start a video+audio stream in a
    * media element, call stop() on the stream, and successfully get an
    * ended event fired.
    */
   runTest(function () {
     getUserMedia({video: true, audio: true}, function(stream) {
       var testVideo = document.getElementById('testVideo');
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
@@ -18,16 +18,23 @@ https://bugzilla.mozilla.org/show_bug.cg
   <audio id="audioLocal" controls></audio>
 
   <video id="videoPCLocal" width="160" height="120" controls></video>
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
+
+if (navigator.platform.indexOf("Win") == 0) {
+  SimpleTest.expectAssertions(0, 9);
+} else {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   var audioLocal;
   var audioPCLocal;
   var audioPCRemote;
 
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
@@ -18,16 +18,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   <audio id="audioLocal" controls></audio>
 
   <video id="videoPCLocal" width="160" height="120" controls></video>
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
+
+if (navigator.platform.indexOf("Linux") != 0) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
   var audioLocal;
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
   var pcLocal;
   var pcRemote;
 
--- a/dom/mms/src/ril/MmsPduHelper.jsm
+++ b/dom/mms/src/ril/MmsPduHelper.jsm
@@ -1575,16 +1575,19 @@ const MMS_PDU_TYPES = (function () {
                                            "x-mms-mms-version",
                                            "x-mms-status"]);
   add(MMS_PDU_TYPE_DELIVERY_IND, false, ["x-mms-message-type",
                                          "x-mms-mms-version",
                                          "message-id",
                                          "to",
                                          "date",
                                          "x-mms-status"]);
+  add(MMS_PDU_TYPE_ACKNOWLEDGE_IND, false, ["x-mms-message-type",
+                                            "x-mms-transaction-id",
+                                            "x-mms-mms-version"]);
 
   return pdus;
 })();
 
 /**
  * Header field names and assigned numbers.
  *
  * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.4
--- a/dom/mms/src/ril/MmsService.js
+++ b/dom/mms/src/ril/MmsService.js
@@ -29,16 +29,22 @@ const HTTP_STATUS_OK = 200;
 const CONFIG_SEND_REPORT_NEVER       = 0;
 const CONFIG_SEND_REPORT_DEFAULT_NO  = 1;
 const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
 const CONFIG_SEND_REPORT_ALWAYS      = 3;
 
 const TIME_TO_BUFFER_MMS_REQUESTS    = 30000;
 const TIME_TO_RELEASE_MMS_CONNECTION = 30000;
 
+
+const PREF_RETRIEVAL_MODE = 'dom.mms.retrieval_mode';
+const RETRIEVAL_MODE_MANUAL = "manual";
+const RETRIEVAL_MODE_AUTOMATIC = "automatic";
+const RETRIEVAL_MODE_NEVER = "never";
+
 XPCOMUtils.defineLazyServiceGetter(this, "gpps",
                                    "@mozilla.org/network/protocol-proxy-service;1",
                                    "nsIProtocolProxyService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
@@ -679,16 +685,58 @@ SendTransaction.prototype = {
 
       let responseStatus = response.headers["x-mms-response-status"];
       callbackIfValid(responseStatus, response);
     });
   }
 };
 
 /**
+ * Send M-acknowledge.ind back to MMSC.
+ *
+ * @param transactionId
+ *        X-Mms-Transaction-ID of the message.
+ * @param reportAllowed
+ *        X-Mms-Report-Allowed of the response.
+ *
+ * @see OMA-TS-MMS_ENC-V1_3-20110913-A section 6.4
+ */
+function AcknowledgeTransaction(transactionId, reportAllowed) {
+  let headers = {};
+
+  // Mandatory fields
+  headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_ACKNOWLEDGE_IND;
+  headers["x-mms-transaction-id"] = transactionId;
+  headers["x-mms-mms-version"] = MMS.MMS_VERSION;
+  // Optional fields
+  headers["x-mms-report-allowed"] = reportAllowed;
+
+  this.istream = MMS.PduHelper.compose(null, {headers: headers});
+}
+AcknowledgeTransaction.prototype = {
+  /**
+   * @param callback [optional]
+   *        A callback function that takes one argument -- the http status.
+   */
+  run: function run(callback) {
+    let requestCallback;
+    if (callback) {
+      requestCallback = function (httpStatus, data) {
+        // `The MMS Client SHOULD ignore the associated HTTP POST response
+        // from the MMS Proxy-Relay.` ~ OMA-TS-MMS_CTR-V1_3-20110913-A
+        // section 8.2.3 "Retrieving an MM".
+        callback(httpStatus);
+      };
+    }
+    gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
+                                      this.istream, requestCallback);
+  }
+};
+
+/**
  * MmsService
  */
 function MmsService() {
   // TODO: bug 810084 - support application identifier
 }
 MmsService.prototype = {
 
   classID:   RIL_MMSSERVICE_CID,
@@ -750,44 +798,63 @@ MmsService.prototype = {
   /**
    * Handle incoming M-Notification.ind PDU.
    *
    * @param notification
    *        The parsed MMS message object.
    */
   handleNotificationIndication: function handleNotificationIndication(notification) {
     // TODO: bug 839436 - make DB be able to save MMS messages
-    // TODO: bug 810067 - support automatic/manual/never retrieval modes
 
     let url = notification.headers["x-mms-content-location"].uri;
     // TODO: bug 810091 - don't download message twice on receiving duplicated
     //                     notification
-    this.retrieveMessage(url, (function (mmsStatus, retrievedMsg) {
-      debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
-      if (this.isTransientError(mmsStatus)) {
-        // TODO: remove this check after bug 810097 is landed.
-        return;
-      }
+
+    let retrievalMode = RETRIEVAL_MODE_MANUAL;
+    try {
+      retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
+    } catch (e) {}
 
-      let transactionId = notification.headers["x-mms-transaction-id"];
+    if (RETRIEVAL_MODE_AUTOMATIC === retrievalMode) {
+      this.retrieveMessage(url, (function responseNotify(mmsStatus, retrievedMsg) {
+        debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
+        if (this.isTransientError(mmsStatus)) {
+          // TODO: remove this check after bug 810097 is landed.
+          return;
+        }
+
+        let transactionId = notification.headers["x-mms-transaction-id"];
 
-      // For X-Mms-Report-Allowed
-      let wish = notification.headers["x-mms-delivery-report"];
-      // `The absence of the field does not indicate any default value.`
-      // So we go checking the same field in retrieved message instead.
-      if ((wish == null) && retrievedMsg) {
-        wish = retrievedMsg.headers["x-mms-delivery-report"];
-      }
-      let reportAllowed =
-        this.getReportAllowed(this.confSendDeliveryReport, wish);
+        // For X-Mms-Report-Allowed
+        let wish = notification.headers["x-mms-delivery-report"];
+        // `The absence of the field does not indicate any default value.`
+        // So we go checking the same field in retrieved message instead.
+        if ((wish == null) && retrievedMsg) {
+          wish = retrievedMsg.headers["x-mms-delivery-report"];
+        }
+        let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish);
 
-      let transaction =
-        new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed);
-      transaction.run();
-    }).bind(this));
+        let transaction =
+          new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed);
+        transaction.run();
+      }).bind(this));
+      return;
+    }
+
+    let transactionId = notification.headers["x-mms-transaction-id"];
+    let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode ?
+        MMS.MMS_PDU_STATUS_REJECTED : MMS.MMS_PDU_STATUS_DEFERRED;
+    // For X-Mms-Report-Allowed
+    let wish = notification.headers["x-mms-delivery-report"];
+    let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish);
+
+    let transaction = new NotifyResponseTransaction(transactionId,
+                                                    mmsStatus,
+                                                    reportAllowed);
+    transaction.run();
   },
 
   /**
    * Handle incoming M-Delivery.ind PDU.
    *
    * @param msg
    *        The MMS message object.
    */
--- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
+++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
@@ -1,33 +1,45 @@
 /* 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 "domstubs.idl"
 #include "nsISupports.idl"
 #include "nsIMobileMessageDatabaseService.idl"
 
-interface nsIDOMMozSmsMessage;
-
-[scriptable, function, uuid(9cd80750-6a08-11e2-ac93-bf895e53f40e)]
+[scriptable, function, uuid(0bffae74-71db-11e2-962a-73cf64d6393e)]
 interface nsIRilMobileMessageDatabaseCallback : nsISupports
 {
-  void notify(in nsresult aRv, in nsIDOMMozSmsMessage aSms);
+  /**
+   * |aRecord| Object: the mobile-message database record
+   */
+  void notify(in nsresult aRv, in jsval aRecord);
 };
 
-[scriptable, uuid(89528354-6a08-11e2-8243-af4cf90404a9)]
+[scriptable, uuid(3592525a-71d6-11e2-82ca-f75ae6e08ee2)]
 interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
 {
-  long saveReceivedMessage(in DOMString aSender,
-                           in DOMString aBody,
-                           in DOMString aMessageClass,
-                           in unsigned long long aDate,
+  /**
+   * |aMessage| Object: should contain the following properties for internal use:
+   *   - |type| DOMString: "sms" or "mms"
+   *   - |sender| DOMString: the phone number of sender
+   *   - |timestamp| Number: the timestamp of received message
+   *   - |messageClass| DOMString: the message class of received message
+   */
+  long saveReceivedMessage(in jsval aMessage,
                 [optional] in nsIRilMobileMessageDatabaseCallback aCallback);
-  long saveSendingMessage(in DOMString aReceiver,
-                          in DOMString aBody,
-                          in DOMString aDeliveryStatus,
-                          in unsigned long long aDate,
+
+  /**
+   * |aMessage| Object: should contain the following properties for internal use:
+   *   - |type| DOMString: "sms" or "mms"
+   *   - |receiver| DOMString: the phone number of receiver
+   *   - |timestamp| Number: the timestamp of sending message
+   *   - |deliveryStatus| DOMString: the delivery status of sending message
+   */
+  long saveSendingMessage(in jsval aMessage,
                [optional] in nsIRilMobileMessageDatabaseCallback aCallback);
+
   void setMessageDelivery(in long aMessageId,
                           in DOMString aDelivery,
                           in DOMString aDeliveryStatus,
                [optional] in nsIRilMobileMessageDatabaseCallback aCallback);
 };
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -408,18 +408,18 @@ MobileMessageDatabaseService.prototype =
         [record.receiver, timestamp]
       ];
       record.readIndex = [record.read, timestamp];
       cursor.update(record);
       cursor.continue();
     };
   },
 
-  createMessageFromRecord: function createMessageFromRecord(aRecord) {
-    if (DEBUG) debug("createMessageFromRecord: " + JSON.stringify(aRecord));
+  createSmsMessageFromRecord: function createSmsMessageFromRecord(aRecord) {
+    if (DEBUG) debug("createSmsMessageFromRecord: " + JSON.stringify(aRecord));
     return gSmsService.createSmsMessage(aRecord.id,
                                         aRecord.delivery,
                                         aRecord.deliveryStatus,
                                         aRecord.sender,
                                         aRecord.receiver,
                                         aRecord.body,
                                         aRecord.messageClass,
                                         aRecord.timestamp,
@@ -477,17 +477,17 @@ MobileMessageDatabaseService.prototype =
     }
 
     let firstMessageId = aMessageList.results.shift();
     if (DEBUG) debug ("Fetching message " + firstMessageId);
 
     let getRequest = aObjectStore.get(firstMessageId);
     let self = this;
     getRequest.onsuccess = function onsuccess(event) {
-      let sms = self.createMessageFromRecord(event.target.result);
+      let sms = self.createSmsMessageFromRecord(event.target.result);
       if (aMessageList.listId >= 0) {
         if (DEBUG) {
           debug("notifyNextMessageInListGot - listId: "
                 + aMessageList.listId + ", messageId: " + firstMessageId);
         }
         smsRequest.notifyNextMessageInListGot(sms);
       } else {
         self.lastMessageListId += 1;
@@ -652,18 +652,17 @@ MobileMessageDatabaseService.prototype =
     aRecord.id = this.lastKey;
     if (DEBUG) debug("Going to store " + JSON.stringify(aRecord));
 
     let self = this;
     function notifyResult(rv) {
       if (!aCallback) {
         return;
       }
-      let sms = self.createMessageFromRecord(aRecord);
-      aCallback.notify(rv, sms);
+      aCallback.notify(rv, aRecord);
     }
 
     this.newTxn(READ_WRITE, function(error, txn, stores) {
       if (error) {
         // TODO bug 832140 check event.target.errorCode
         notifyResult(Cr.NS_ERROR_FAILURE);
         return;
       }
@@ -673,19 +672,18 @@ MobileMessageDatabaseService.prototype =
       txn.onabort = function onabort(event) {
         // TODO bug 832140 check event.target.errorCode
         notifyResult(Cr.NS_ERROR_FAILURE);
       };
 
       // First add to main objectStore.
       stores[0].put(aRecord);
 
+      // Next update the other objectStore.
       let number = getNumberFromRecord(aRecord);
-
-      // Next update the other objectStore.
       stores[1].get(number).onsuccess = function onsuccess(event) {
         let mostRecentEntry = event.target.result;
         if (mostRecentEntry) {
           let needsUpdate = false;
 
           if (mostRecentEntry.timestamp <= aRecord.timestamp) {
             mostRecentEntry.timestamp = aRecord.timestamp;
             mostRecentEntry.body = aRecord.body;
@@ -708,130 +706,122 @@ MobileMessageDatabaseService.prototype =
                                     unreadCount: aRecord.read ? 0 : 1 });
         }
       };
     }, [STORE_NAME, MOST_RECENT_STORE_NAME]);
     // We return the key that we expect to store in the db
     return aRecord.id;
   },
 
+  getRilIccInfoMsisdn: function getRilIccInfoMsisdn() {
+    let iccInfo = this.mRIL.rilContext.iccInfo;
+    let number = iccInfo ? iccInfo.msisdn : null;
+
+    // Workaround an xpconnect issue with undefined string objects.
+    // See bug 808220
+    if (number === undefined || number === "undefined") {
+      return null;
+    }
+    return number;
+  },
+
+  makePhoneNumberInternational: function makePhoneNumberInternational(aNumber) {
+    if (!aNumber) {
+      return aNumber;
+    }
+    let parsedNumber = PhoneNumberUtils.parse(aNumber.toString());
+    if (!parsedNumber || !parsedNumber.internationalNumber) {
+      return aNumber;
+    }
+    return parsedNumber.internationalNumber;
+  },
 
   /**
    * nsIRilMobileMessageDatabaseService API
    */
 
-  saveReceivedMessage: function saveReceivedMessage(
-      aSender, aBody, aMessageClass, aDate, aCallback) {
-    let iccInfo = this.mRIL.rilContext.iccInfo;
-    let receiver = iccInfo ? iccInfo.msisdn : null;
-
-    // Workaround an xpconnect issue with undefined string objects.
-    // See bug 808220
-    if (receiver === undefined || receiver === "undefined") {
-      receiver = null;
-    }
-
-    if (receiver) {
-      let parsedNumber = PhoneNumberUtils.parse(receiver);
-      receiver = (parsedNumber && parsedNumber.internationalNumber)
-                 ? parsedNumber.internationalNumber
-                 : receiver;
+  saveReceivedMessage: function saveReceivedMessage(aMessage, aCallback) {
+    if (aMessage.type === undefined ||
+        aMessage.sender === undefined ||
+        aMessage.messageClass === undefined ||
+        aMessage.timestamp === undefined) {
+      if (aCallback) {
+        aCallback.notify(Cr.NS_ERROR_FAILURE, null);
+      }
+      return;
     }
 
-    let sender = aSender;
-    if (sender) {
-      let parsedNumber = PhoneNumberUtils.parse(sender);
-      sender = (parsedNumber && parsedNumber.internationalNumber)
-               ? parsedNumber.internationalNumber
-               : sender;
-    }
+    let receiver = this.getRilIccInfoMsisdn();
+    receiver = this.makePhoneNumberInternational(receiver);
+
+    let sender = aMessage.sender =
+      this.makePhoneNumberInternational(aMessage.sender);
+
+    let timestamp = aMessage.timestamp;
 
-    let record = {
-      deliveryIndex:  [DELIVERY_RECEIVED, aDate],
-      numberIndex:    [[sender, aDate], [receiver, aDate]],
-      readIndex:      [FILTER_READ_UNREAD, aDate],
+    // Adding needed indexes and extra attributes for internal use.
+    aMessage.deliveryIndex = [DELIVERY_RECEIVED, timestamp];
+    aMessage.numberIndex = [[sender, timestamp], [receiver, timestamp]];
+    aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
+    aMessage.delivery = DELIVERY_RECEIVED;
+    aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
+    aMessage.receiver = receiver;
+    aMessage.read = FILTER_READ_UNREAD;
 
-      delivery:       DELIVERY_RECEIVED,
-      deliveryStatus: DELIVERY_STATUS_SUCCESS,
-      sender:         sender,
-      receiver:       receiver,
-      body:           aBody,
-      messageClass:   aMessageClass,
-      timestamp:      aDate,
-      read:           FILTER_READ_UNREAD
-    };