merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 22 Jul 2016 11:58:02 +0200
changeset 346263 e0bc88708ffed39aaab1fbc0ac461d93561195de
parent 346193 81539328348ac4b64ec580c45e825353d00e96d8 (current diff)
parent 346262 968c7be2c2a62daedf768fdb1c8ac316220c2895 (diff)
child 346264 7c669d5d63efceb12696cd65cfa72c296013dafb
child 346265 6776f3d11ac449add269876aea550a08dba9ee45
child 346326 5903f05d003ba5fff6afddc0a87666fbcc0d5a66
child 346383 30745bed2567f484ff609a6d389a0e57a1b4a8d5
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
e0bc88708ffe / 50.0a1 / 20160722030235 / files
nightly linux64
e0bc88708ffe / 50.0a1 / 20160722030235 / files
nightly mac
e0bc88708ffe / 50.0a1 / 20160722030235 / files
nightly win32
e0bc88708ffe / 50.0a1 / 20160722030235 / files
nightly win64
e0bc88708ffe / 50.0a1 / 20160722030235 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/media/mediasource/MediaSource.cpp
extensions/spellcheck/hunspell/src/moz.build
gfx/layers/moz.build
gfx/thebes/gfxPlatform.cpp
layout/style/moz.build
media/libstagefright/moz.build
media/libvpx/moz.build
testing/web-platform/meta/media-source/URL-createObjectURL.html.ini
--- a/.eslintignore
+++ b/.eslintignore
@@ -94,16 +94,17 @@ devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/*.jsm
 devtools/client/shared/webgl-utils.js
 devtools/client/shared/developer-toolbar.js
 devtools/client/shared/components/test/**
 devtools/client/shared/redux/middleware/test/**
 devtools/client/shared/test/**
+!devtools/client/shared/test/test-actor-registry.js
 devtools/client/shared/widgets/*.jsm
 devtools/client/sourceeditor/**
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/**
 !devtools/client/webconsole/panel.js
 !devtools/client/webconsole/jsterm.js
 !devtools/client/webconsole/console-commands.js
 devtools/client/webide/**
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 911216 - clobber needed
+Bug 1287946 - clobber due to generated SDK headers changing (bug 1182840)
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -859,37 +859,73 @@ struct RoleComparator
   }
 };
 
 }
 
 const nsRoleMapEntry*
 aria::GetRoleMap(dom::Element* aEl)
 {
+  return GetRoleMapFromIndex(GetRoleMapIndex(aEl));
+}
+
+uint8_t
+aria::GetRoleMapIndex(dom::Element* aEl)
+{
   nsAutoString roles;
   if (!aEl || !aEl->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
       roles.IsEmpty()) {
     // We treat role="" as if the role attribute is absent (per aria spec:8.1.1)
-    return nullptr;
+    return NO_ROLE_MAP_ENTRY_INDEX;
   }
 
   nsWhitespaceTokenizer tokenizer(roles);
   while (tokenizer.hasMoreTokens()) {
     // Do a binary search through table for the next role in role list
     const nsDependentSubstring role = tokenizer.nextToken();
     size_t idx;
     if (BinarySearchIf(sWAIRoleMaps, 0, ArrayLength(sWAIRoleMaps),
                        RoleComparator(role), &idx)) {
-      return sWAIRoleMaps + idx;
+      return idx;
     }
   }
 
-  // Always use some entry if there is a non-empty role string
+  // Always use some entry index if there is a non-empty role string
   // To ensure an accessible object is created
-  return &sLandmarkRoleMap;
+  return LANDMARK_ROLE_MAP_ENTRY_INDEX;
+}
+
+
+const nsRoleMapEntry*
+aria::GetRoleMapFromIndex(uint8_t aRoleMapIndex)
+{
+  switch (aRoleMapIndex) {
+    case NO_ROLE_MAP_ENTRY_INDEX:
+      return nullptr;
+    case EMPTY_ROLE_MAP_ENTRY_INDEX:
+      return &gEmptyRoleMap;
+    case LANDMARK_ROLE_MAP_ENTRY_INDEX:
+      return &sLandmarkRoleMap;
+    default:
+      return sWAIRoleMaps + aRoleMapIndex;
+  }
+}
+
+uint8_t
+aria::GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMapEntry)
+{
+  if (aRoleMapEntry == nullptr) {
+    return NO_ROLE_MAP_ENTRY_INDEX;
+  } else if (aRoleMapEntry == &gEmptyRoleMap) {
+    return EMPTY_ROLE_MAP_ENTRY_INDEX;
+  } else if (aRoleMapEntry == &sLandmarkRoleMap) {
+      return LANDMARK_ROLE_MAP_ENTRY_INDEX;
+  } else {
+    return aRoleMapEntry - sWAIRoleMaps;
+  }
 }
 
 uint64_t
 aria::UniversalStatesFor(mozilla::dom::Element* aElement)
 {
   uint64_t state = 0;
   uint32_t index = 0;
   while (MapToState(sWAIUnivStateMap[index], aElement, &state))
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -198,26 +198,67 @@ namespace aria {
 /**
  * Empty role map entry. Used by accessibility service to create an accessible
  * if the accessible can't use role of used accessible class. For example,
  * it is used for table cells that aren't contained by table.
  */
 extern nsRoleMapEntry gEmptyRoleMap;
 
 /**
+ * Constants for the role map entry index to indicate that the role map entry
+ * isn't in sWAIRoleMaps, but rather is a special entry: nullptr,
+ * gEmptyRoleMap, and sLandmarkRoleMap
+ */
+const uint8_t NO_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 2;
+const uint8_t EMPTY_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 1;
+const uint8_t LANDMARK_ROLE_MAP_ENTRY_INDEX = UINT8_MAX;
+
+/**
  * Get the role map entry for a given DOM node. This will use the first
  * ARIA role if the role attribute provides a space delimited list of roles.
  *
- * @param aNode  [in] the DOM node to get the role map entry for
+ * @param aEl     [in] the DOM node to get the role map entry for
  * @return        a pointer to the role map entry for the ARIA role, or nullptr
  *                if none
  */
 const nsRoleMapEntry* GetRoleMap(dom::Element* aEl);
 
 /**
+ * Get the role map entry pointer's index for a given DOM node. This will use
+ * the first ARIA role if the role attribute provides a space delimited list of
+ * roles.
+ *
+ * @param aEl     [in] the DOM node to get the role map entry for
+ * @return        the index of the pointer to the role map entry for the ARIA
+ *                role, or NO_ROLE_MAP_ENTRY_INDEX if none
+ */
+uint8_t GetRoleMapIndex(dom::Element* aEl);
+
+/**
+ * Get the role map entry pointer for a given role map entry index.
+ *
+ * @param aRoleMapIndex  [in] the role map index to get the role map entry
+ *                       pointer for
+ * @return               a pointer to the role map entry for the ARIA role,
+ *                       or nullptr, if none
+ */
+const nsRoleMapEntry* GetRoleMapFromIndex(uint8_t aRoleMapIndex);
+
+/**
+ * Get the role map entry index for a given role map entry pointer. If the role
+ * map entry is within sWAIRoleMaps, return the index within that array,
+ * otherwise return one of the special index constants listed above.
+ *
+ * @param aRoleMap  [in] the role map entry pointer to get the index for
+ * @return          the index of the pointer to the role map entry, or
+ *                  NO_ROLE_MAP_ENTRY_INDEX if none
+ */
+uint8_t GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMap);
+
+/**
  * Return accessible state from ARIA universal states applied to the given
  * element.
  */
 uint64_t UniversalStatesFor(mozilla::dom::Element* aElement);
 
 /**
  * Get the ARIA attribute characteristics for a given ARIA attribute.
  *
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -16,66 +16,91 @@
 #endif
 
 namespace mozilla {
 namespace a11y {
 
 inline mozilla::a11y::role
 Accessible::Role()
 {
-  if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry || roleMapEntry->roleRule != kUseMapRole)
     return ARIATransformRole(NativeRole());
 
-  return ARIATransformRole(mRoleMapEntry->role);
+  return ARIATransformRole(roleMapEntry->role);
+}
+
+inline bool
+Accessible::HasARIARole() const
+{
+  return mRoleMapEntryIndex != aria::NO_ROLE_MAP_ENTRY_INDEX;
 }
 
 inline bool
 Accessible::IsARIARole(nsIAtom* aARIARole) const
 {
-  return mRoleMapEntry && mRoleMapEntry->Is(aARIARole);
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  return roleMapEntry && roleMapEntry->Is(aARIARole);
 }
 
 inline bool
 Accessible::HasStrongARIARole() const
 {
-  return mRoleMapEntry && mRoleMapEntry->roleRule == kUseMapRole;
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  return roleMapEntry && roleMapEntry->roleRule == kUseMapRole;
+}
+
+inline const nsRoleMapEntry*
+Accessible::ARIARoleMap() const
+{
+  return aria::GetRoleMapFromIndex(mRoleMapEntryIndex);
 }
 
 inline mozilla::a11y::role
 Accessible::ARIARole()
 {
-  if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry || roleMapEntry->roleRule != kUseMapRole)
     return mozilla::a11y::roles::NOTHING;
 
-  return ARIATransformRole(mRoleMapEntry->role);
+  return ARIATransformRole(roleMapEntry->role);
+}
+
+inline void
+Accessible::SetRoleMapEntry(const nsRoleMapEntry* aRoleMapEntry)
+{
+  mRoleMapEntryIndex = aria::GetIndexFromRoleMap(aRoleMapEntry);
 }
 
 inline bool
 Accessible::IsSearchbox() const
 {
-  return (mRoleMapEntry && mRoleMapEntry->Is(nsGkAtoms::searchbox)) ||
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  return (roleMapEntry && roleMapEntry->Is(nsGkAtoms::searchbox)) ||
     (mContent->IsHTMLElement(nsGkAtoms::input) &&
      mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            nsGkAtoms::textInputType, eCaseMatters));
 }
 
 inline bool
 Accessible::HasGenericType(AccGenericType aType) const
 {
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
   return (mGenericTypes & aType) ||
-    (mRoleMapEntry && mRoleMapEntry->IsOfType(aType));
+    (roleMapEntry && roleMapEntry->IsOfType(aType));
 }
 
 inline bool
 Accessible::HasNumericValue() const
 {
   if (mStateFlags & eHasNumericValue)
     return true;
 
-  return mRoleMapEntry && mRoleMapEntry->valueRule != eNoValue;
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  return roleMapEntry && roleMapEntry->valueRule != eNoValue;
 }
 
 inline void
 Accessible::ScrollTo(uint32_t aHow) const
 {
   if (mContent)
     nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
 }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -103,18 +103,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
 
 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
   mContent(aContent), mDoc(aDoc),
   mParent(nullptr), mIndexInParent(-1),
-  mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
-  mRoleMapEntry(nullptr)
+  mRoleMapEntryIndex(aria::NO_ROLE_MAP_ENTRY_INDEX),
+  mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0)
 {
   mBits.groupInfo = nullptr;
   mInt.mIndexOfEmbeddedChild = -1;
 }
 
 Accessible::~Accessible()
 {
   NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
@@ -437,18 +437,19 @@ Accessible::NativeState()
   }
 
   // Check if a XUL element has the popup attribute (an attached popup menu).
   if (HasOwnContent() && mContent->IsXULElement() &&
       mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
     state |= states::HASPOPUP;
 
   // Bypass the link states specialization for non links.
-  if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
-      mRoleMapEntry->role == roles::LINK)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry || roleMapEntry->roleRule == kUseNativeRole ||
+      roleMapEntry->role == roles::LINK)
     state |= NativeLinkState();
 
   return state;
 }
 
 uint64_t
 Accessible::NativeInteractiveState() const
 {
@@ -685,17 +686,17 @@ void
 Accessible::SetSelected(bool aSelect)
 {
   if (!HasOwnContent())
     return;
 
   Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
   if (select) {
     if (select->State() & states::MULTISELECTABLE) {
-      if (mRoleMapEntry) {
+      if (ARIARoleMap()) {
         if (aSelect) {
           mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
                             NS_LITERAL_STRING("true"), true);
         } else {
           mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true);
         }
       }
       return;
@@ -924,26 +925,27 @@ Accessible::Attributes()
 
   if (IsARIAHidden()) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
                            NS_LITERAL_STRING("true"));
   }
 
   // If there is no aria-live attribute then expose default value of 'live'
   // object attribute used for ARIA role of this accessible.
-  if (mRoleMapEntry) {
-    if (mRoleMapEntry->Is(nsGkAtoms::searchbox)) {
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry) {
+    if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
                              NS_LITERAL_STRING("search"));
     }
 
     nsAutoString live;
     nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
     if (live.IsEmpty()) {
-      if (nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live))
+      if (nsAccUtils::GetLiveAttrValue(roleMapEntry->liveAttRule, live))
         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
     }
   }
 
   return attributes.forget();
 }
 
 already_AddRefed<nsIPersistentProperties>
@@ -1138,23 +1140,24 @@ Accessible::State()
 
   uint64_t state = NativeState();
   // Apply ARIA states to be sure accessible states will be overridden.
   ApplyARIAState(&state);
 
   // If this is an ARIA item of the selectable widget and if it's focused and
   // not marked unselected explicitly (i.e. aria-selected="false") then expose
   // it as selected to make ARIA widget authors life easier.
-  if (mRoleMapEntry && !(state & states::SELECTED) &&
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry && !(state & states::SELECTED) &&
       !mContent->AttrValueIs(kNameSpaceID_None,
                              nsGkAtoms::aria_selected,
                              nsGkAtoms::_false, eCaseMatters)) {
     // Special case for tabs: focused tab or focus inside related tab panel
     // implies selected state.
-    if (mRoleMapEntry->role == roles::PAGETAB) {
+    if (roleMapEntry->role == roles::PAGETAB) {
       if (state & states::FOCUSED) {
         state |= states::SELECTED;
       } else {
         // If focus is in a child of the tab panel surely the tab is selected!
         Relation rel = RelationByType(RelationType::LABEL_FOR);
         Accessible* relTarget = nullptr;
         while ((relTarget = rel.Next())) {
           if (relTarget->Role() == roles::PROPERTYPAGE &&
@@ -1217,22 +1220,23 @@ Accessible::ApplyARIAState(uint64_t* aSt
   if (!mContent->IsElement())
     return;
 
   dom::Element* element = mContent->AsElement();
 
   // Test for universal states first
   *aState |= aria::UniversalStatesFor(element);
 
-  if (mRoleMapEntry) {
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry) {
 
     // We only force the readonly bit off if we have a real mapping for the aria
     // role. This preserves the ability for screen readers to use readonly
     // (primarily on the document) as the hint for creating a virtual buffer.
-    if (mRoleMapEntry->role != roles::NOTHING)
+    if (roleMapEntry->role != roles::NOTHING)
       *aState &= ~states::READONLY;
 
     if (mContent->HasID()) {
       // If has a role & ID and aria-activedescendant on the container, assume
       // focusable.
       const Accessible* ancestor = this;
       while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
         dom::Element* el = ancestor->Elm();
@@ -1258,31 +1262,31 @@ Accessible::ApplyARIAState(uint64_t* aSt
     }
   }
 
   // special case: A native button element whose role got transformed by ARIA to a toggle button
   // Also applies to togglable button menus, like in the Dev Tools Web Console.
   if (IsButton() || IsMenuButton())
     aria::MapToState(aria::eARIAPressed, element, aState);
 
-  if (!mRoleMapEntry)
+  if (!roleMapEntry)
     return;
 
-  *aState |= mRoleMapEntry->state;
-
-  if (aria::MapToState(mRoleMapEntry->attributeMap1, element, aState) &&
-      aria::MapToState(mRoleMapEntry->attributeMap2, element, aState) &&
-      aria::MapToState(mRoleMapEntry->attributeMap3, element, aState))
-    aria::MapToState(mRoleMapEntry->attributeMap4, element, aState);
+  *aState |= roleMapEntry->state;
+
+  if (aria::MapToState(roleMapEntry->attributeMap1, element, aState) &&
+      aria::MapToState(roleMapEntry->attributeMap2, element, aState) &&
+      aria::MapToState(roleMapEntry->attributeMap3, element, aState))
+    aria::MapToState(roleMapEntry->attributeMap4, element, aState);
 
   // ARIA gridcell inherits editable/readonly states from the grid until it's
   // overridden.
-  if ((mRoleMapEntry->Is(nsGkAtoms::gridcell) ||
-       mRoleMapEntry->Is(nsGkAtoms::columnheader) ||
-       mRoleMapEntry->Is(nsGkAtoms::rowheader)) &&
+  if ((roleMapEntry->Is(nsGkAtoms::gridcell) ||
+       roleMapEntry->Is(nsGkAtoms::columnheader) ||
+       roleMapEntry->Is(nsGkAtoms::rowheader)) &&
       !(*aState & (states::READONLY | states::EDITABLE))) {
     const TableCellAccessible* cell = AsTableCell();
     if (cell) {
       TableAccessible* table = cell->Table();
       if (table) {
         Accessible* grid = table->AsAccessible();
         uint64_t gridState = 0;
         grid->ApplyARIAState(&gridState);
@@ -1290,39 +1294,40 @@ Accessible::ApplyARIAState(uint64_t* aSt
       }
     }
   }
 }
 
 void
 Accessible::Value(nsString& aValue)
 {
-  if (!mRoleMapEntry)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry)
     return;
 
-  if (mRoleMapEntry->valueRule != eNoValue) {
+  if (roleMapEntry->valueRule != eNoValue) {
     // aria-valuenow is a number, and aria-valuetext is the optional text
     // equivalent. For the string value, we will try the optional text
     // equivalent first.
     if (!mContent->GetAttr(kNameSpaceID_None,
                            nsGkAtoms::aria_valuetext, aValue)) {
       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
                         aValue);
     }
     return;
   }
 
   // Value of textbox is a textified subtree.
-  if (mRoleMapEntry->Is(nsGkAtoms::textbox)) {
+  if (roleMapEntry->Is(nsGkAtoms::textbox)) {
     nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
     return;
   }
 
   // Value of combobox is a text of current or selected item.
-  if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
+  if (roleMapEntry->Is(nsGkAtoms::combobox)) {
     Accessible* option = CurrentItem();
     if (!option) {
       uint32_t childCount = ChildCount();
       for (uint32_t idx = 0; idx < childCount; idx++) {
         Accessible* child = mChildren.ElementAt(idx);
         if (child->IsListControl()) {
           option = child->GetSelectedItem(0);
           break;
@@ -1357,17 +1362,18 @@ double
 Accessible::CurValue() const
 {
   return AttrNumericValue(nsGkAtoms::aria_valuenow);
 }
 
 bool
 Accessible::SetCurValue(double aValue)
 {
-  if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
     return false;
 
   const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE;
   if (State() & kValueCannotChange)
     return false;
 
   double checkValue = MinValue();
   if (!IsNaN(checkValue) && aValue < checkValue)
@@ -1431,18 +1437,19 @@ Accessible::ARIATransformRole(role aRole
   }
 
   return aRole;
 }
 
 nsIAtom*
 Accessible::LandmarkRole() const
 {
-  return mRoleMapEntry && mRoleMapEntry->IsOfType(eLandmark) ?
-    *(mRoleMapEntry->roleAtom) : nullptr;
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  return roleMapEntry && roleMapEntry->IsOfType(eLandmark) ?
+    *(roleMapEntry->roleAtom) : nullptr;
 }
 
 role
 Accessible::NativeRole()
 {
   return roles::NOTHING;
 }
 
@@ -1545,16 +1552,18 @@ Accessible::GetAtomicRegion() const
 }
 
 Relation
 Accessible::RelationByType(RelationType aType)
 {
   if (!HasOwnContent())
     return Relation();
 
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+
   // Relationships are defined on the same content node that the role would be
   // defined on.
   switch (aType) {
     case RelationType::LABELLED_BY: {
       Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_labelledby));
       if (mContent->IsHTMLElement()) {
         rel.AppendIter(new HTMLLabelIterator(Document(), this));
@@ -1596,19 +1605,19 @@ Accessible::RelationByType(RelationType 
 
       return rel;
     }
 
     case RelationType::NODE_CHILD_OF: {
       Relation rel;
       // 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::LISTITEM ||
-                            mRoleMapEntry->role == roles::ROW)) {
+      if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
+                            roleMapEntry->role == roles::LISTITEM ||
+                            roleMapEntry->role == roles::ROW)) {
         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
@@ -1624,23 +1633,23 @@ Accessible::RelationByType(RelationType 
       }
 
       return rel;
     }
 
     case RelationType::NODE_PARENT_OF: {
       // 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::LISTITEM ||
-           mRoleMapEntry->role == roles::ROW ||
-           mRoleMapEntry->role == roles::OUTLINE ||
-           mRoleMapEntry->role == roles::LIST ||
-           mRoleMapEntry->role == roles::TREE_TABLE)) {
+      if (roleMapEntry &&
+          (roleMapEntry->role == roles::OUTLINEITEM ||
+           roleMapEntry->role == roles::LISTITEM ||
+           roleMapEntry->role == roles::ROW ||
+           roleMapEntry->role == roles::OUTLINE ||
+           roleMapEntry->role == roles::LIST ||
+           roleMapEntry->role == roles::TREE_TABLE)) {
         return Relation(new ItemIterator(this));
       }
 
       return Relation();
     }
 
     case RelationType::CONTROLLED_BY:
       return Relation(new RelatedAccIterator(Document(), mContent,
@@ -2438,17 +2447,18 @@ Accessible::IsWidget() const
 bool
 Accessible::IsActiveWidget() const
 {
   if (FocusMgr()->HasDOMFocus(mContent))
     return true;
 
   // If text entry of combobox widget has a focus then the combobox widget is
   // active.
-  if (mRoleMapEntry && mRoleMapEntry->Is(nsGkAtoms::combobox)) {
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::combobox)) {
     uint32_t childCount = ChildCount();
     for (uint32_t idx = 0; idx < childCount; idx++) {
       Accessible* child = mChildren.ElementAt(idx);
       if (child->Role() == roles::ENTRY)
         return FocusMgr()->HasDOMFocus(child->GetContent());
     }
   }
 
@@ -2567,17 +2577,18 @@ Accessible::GetSiblingAtOffset(int32_t a
     *aError = NS_ERROR_UNEXPECTED;
 
   return child;
 }
 
 double
 Accessible::AttrNumericValue(nsIAtom* aAttr) const
 {
-  if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
     return UnspecifiedNaN<double>();
 
   nsAutoString attrValue;
   if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
     return UnspecifiedNaN<double>();
 
   nsresult error = NS_OK;
   double value = attrValue.ToDouble(&error);
@@ -2597,19 +2608,20 @@ Accessible::GetActionRule() const
 
   // Has registered 'click' event handler.
   bool isOnclick = nsCoreUtils::HasClickListener(mContent);
 
   if (isOnclick)
     return eClickAction;
 
   // Get an action based on ARIA role.
-  if (mRoleMapEntry &&
-      mRoleMapEntry->actionRule != eNoAction)
-    return mRoleMapEntry->actionRule;
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry &&
+      roleMapEntry->actionRule != eNoAction)
+    return roleMapEntry->actionRule;
 
   // Get an action based on ARIA attribute.
   if (nsAccUtils::HasDefinedARIAToken(mContent,
                                       nsGkAtoms::aria_expanded))
     return eExpandAction;
 
   return eNoAction;
 }
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -225,24 +225,24 @@ public:
   /**
    * Return enumerated accessible role (see constants in Role.h).
    */
   mozilla::a11y::role Role();
 
   /**
    * Return true if ARIA role is specified on the element.
    */
-  bool HasARIARole() const { return mRoleMapEntry; }
+  bool HasARIARole() const;
   bool IsARIARole(nsIAtom* aARIARole) const;
   bool HasStrongARIARole() const;
 
   /**
    * Retrun ARIA role map if any.
    */
-  const nsRoleMapEntry* ARIARoleMap() const { return mRoleMapEntry; }
+  const nsRoleMapEntry* ARIARoleMap() const;
 
   /**
    * Return accessible role specified by ARIA (see constants in
    * roles).
    */
   mozilla::a11y::role ARIARole();
 
   /**
@@ -378,18 +378,17 @@ public:
   /**
    * Shutdown this accessible object.
    */
   virtual void Shutdown();
 
   /**
    * Set the ARIA role map entry for a new accessible.
    */
-  void SetRoleMapEntry(const nsRoleMapEntry* aRoleMapEntry)
-    { mRoleMapEntry = aRoleMapEntry; }
+  void SetRoleMapEntry(const nsRoleMapEntry* aRoleMapEntry);
 
   /**
    * Append/insert/remove a child. Return true if operation was successful.
    */
   bool AppendChild(Accessible* aChild)
     { return InsertChildAt(mChildren.Length(), aChild); }
   virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild);
 
@@ -1115,16 +1114,22 @@ protected:
   int32_t mIndexInParent;
 
   static const uint8_t kStateFlagsBits = 13;
   static const uint8_t kContextFlagsBits = 3;
   static const uint8_t kTypeBits = 6;
   static const uint8_t kGenericTypesBits = 16;
 
   /**
+   * Non-NO_ROLE_MAP_ENTRY_INDEX indicates author-supplied role;
+   * possibly state & value as well
+   */
+  uint8_t mRoleMapEntryIndex;
+
+  /**
    * Keep in sync with StateFlags, ContextFlags, and AccTypes.
    */
   uint32_t mStateFlags : kStateFlagsBits;
   uint32_t mContextFlags : kContextFlagsBits;
   uint32_t mType : kTypeBits;
   uint32_t mGenericTypes : kGenericTypesBits;
 
   void StaticAsserts() const;
@@ -1149,21 +1154,16 @@ protected:
 
   union
   {
     AccGroupInfo* groupInfo;
     ProxyAccessible* proxy;
   } mBits;
   friend class AccGroupInfo;
 
-  /**
-   * Non-null indicates author-supplied role; possibly state & value as well
-   */
-  const nsRoleMapEntry* mRoleMapEntry;
-
 private:
   Accessible() = delete;
   Accessible(const Accessible&) = delete;
   Accessible& operator =(const Accessible&) = delete;
 
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Accessible,
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -2212,18 +2212,19 @@ HyperTextAccessible::GetSpellTextAttr(ns
 
   if (startOffset > *aStartOffset)
     *aStartOffset = startOffset;
 }
 
 bool
 HyperTextAccessible::IsTextRole()
 {
-  if (mRoleMapEntry &&
-      (mRoleMapEntry->role == roles::GRAPHIC ||
-       mRoleMapEntry->role == roles::IMAGE_MAP ||
-       mRoleMapEntry->role == roles::SLIDER ||
-       mRoleMapEntry->role == roles::PROGRESSBAR ||
-       mRoleMapEntry->role == roles::SEPARATOR))
+  const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+  if (roleMapEntry &&
+      (roleMapEntry->role == roles::GRAPHIC ||
+       roleMapEntry->role == roles::IMAGE_MAP ||
+       roleMapEntry->role == roles::SLIDER ||
+       roleMapEntry->role == roles::PROGRESSBAR ||
+       roleMapEntry->role == roles::SEPARATOR))
     return false;
 
   return true;
 }
--- a/accessible/generic/RootAccessible.cpp
+++ b/accessible/generic/RootAccessible.cpp
@@ -72,17 +72,17 @@ RootAccessible::~RootAccessible()
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible
 
 ENameValueFlag
 RootAccessible::Name(nsString& aName)
 {
   aName.Truncate();
 
-  if (mRoleMapEntry) {
+  if (ARIARoleMap()) {
     Accessible::Name(aName);
     if (!aName.IsEmpty())
       return eNameOK;
   }
 
   mDocumentNode->GetTitle(aName);
   return eNameOK;
 }
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -303,17 +303,17 @@ HTMLTextFieldAccessible::NativeAttribute
   nsCOMPtr<nsIPersistentProperties> attributes =
     HyperTextAccessibleWrap::NativeAttributes();
 
   // Expose type for text input elements as it gives some useful context,
   // especially for mobile.
   nsAutoString type;
   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType, type);
-    if (!mRoleMapEntry && type.EqualsLiteral("search")) {
+    if (!ARIARoleMap() && type.EqualsLiteral("search")) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                              NS_LITERAL_STRING("searchbox"));
     }
   }
 
   return attributes.forget();
 }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -592,24 +592,20 @@ nsContextMenu.prototype = {
 
     return gDevTools.showToolbox(target, "inspector").then(toolbox => {
       let inspector = toolbox.getCurrentPanel();
 
       // new-node-front tells us when the node has been selected, whether the
       // browser is remote or not.
       let onNewNode = inspector.selection.once("new-node-front");
 
-      if (this.isRemote) {
-        this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
-        inspector.walker.findInspectingNode().then(nodeFront => {
-          inspector.selection.setNodeFront(nodeFront, "browser-context-menu");
-        });
-      } else {
-        inspector.selection.setNode(this.target, "browser-context-menu");
-      }
+      this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
+      inspector.walker.findInspectingNode().then(nodeFront => {
+        inspector.selection.setNodeFront(nodeFront, "browser-context-menu");
+      });
 
       return onNewNode.then(() => {
         // Now that the node has been selected, wait until the inspector is
         // fully updated.
         return inspector.once("inspector-updated");
       });
     });
   },
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -5,101 +5,163 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["BrowserUsageTelemetry"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+// The upper bound for the count of the visited unique domain names.
+const MAX_UNIQUE_VISITED_DOMAINS = 100;
 
 // Observed topic names.
 const WINDOWS_RESTORED_TOPIC = "sessionstore-windows-restored";
 const TELEMETRY_SUBSESSIONSPLIT_TOPIC = "internal-telemetry-after-subsession-split";
 const DOMWINDOW_OPENED_TOPIC = "domwindowopened";
-const DOMWINDOW_CLOSED_TOPIC = "domwindowclosed";
 
 // Probe names.
 const MAX_TAB_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_tab_count";
 const MAX_WINDOW_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_window_count";
 const TAB_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.tab_open_event_count";
 const WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.window_open_event_count";
+const UNIQUE_DOMAINS_COUNT_SCALAR_NAME = "browser.engagement.unique_domains_count";
+const TOTAL_URI_COUNT_SCALAR_NAME = "browser.engagement.total_uri_count";
 
 function getOpenTabsAndWinsCounts() {
   let tabCount = 0;
   let winCount = 0;
 
   let browserEnum = Services.wm.getEnumerator("navigator:browser");
   while (browserEnum.hasMoreElements()) {
     let win = browserEnum.getNext();
     winCount++;
     tabCount += win.gBrowser.tabs.length;
   }
 
   return { tabCount, winCount };
 }
 
+let URICountListener = {
+  // A set containing the visited domains, see bug 1271310.
+  _domainSet: new Set(),
+
+  onLocationChange(browser, webProgress, request, uri, flags) {
+    // Don't count this URI if it's an error page.
+    if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
+      return;
+    }
+
+    // We only care about top level loads.
+    if (!webProgress.isTopLevel) {
+      return;
+    }
+
+    // Only consider http(s) schemas.
+    if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
+      return;
+    }
+
+    // Update the URI counts.
+    Services.telemetry.scalarAdd(TOTAL_URI_COUNT_SCALAR_NAME, 1);
+
+    // We only want to count the unique domains up to MAX_UNIQUE_VISITED_DOMAINS.
+    if (this._domainSet.size == MAX_UNIQUE_VISITED_DOMAINS) {
+      return;
+    }
+
+    // Unique domains should be aggregated by (eTLD + 1): x.test.com and y.test.com
+    // are counted once as test.com.
+    try {
+      // Even if only considering http(s) URIs, |getBaseDomain| could still throw
+      // due to the URI containing invalid characters or the domain actually being
+      // an ipv4 or ipv6 address.
+      this._domainSet.add(Services.eTLD.getBaseDomain(uri));
+    } catch (e) {
+      return;
+    }
+
+    Services.telemetry.scalarSet(UNIQUE_DOMAINS_COUNT_SCALAR_NAME, this._domainSet.size);
+  },
+
+  /**
+   * Reset the counts. This should be called when breaking a session in Telemetry.
+   */
+  reset() {
+    this._domainSet.clear();
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference]),
+};
+
 let BrowserUsageTelemetry = {
   init() {
     Services.obs.addObserver(this, WINDOWS_RESTORED_TOPIC, false);
   },
 
   /**
    * Handle subsession splits in the parent process.
    */
   afterSubsessionSplit() {
     // Scalars just got cleared due to a subsession split. We need to set the maximum
     // concurrent tab and window counts so that they reflect the correct value for the
     // new subsession.
     const counts = getOpenTabsAndWinsCounts();
     Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
     Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
+
+    // Reset the URI counter.
+    URICountListener.reset();
   },
 
   uninit() {
     Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC, false);
-    Services.obs.removeObserver(this, DOMWINDOW_CLOSED_TOPIC, false);
     Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
     Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC, false);
   },
 
   observe(subject, topic, data) {
     switch(topic) {
       case WINDOWS_RESTORED_TOPIC:
         this._setupAfterRestore();
         break;
       case DOMWINDOW_OPENED_TOPIC:
         this._onWindowOpen(subject);
         break;
-      case DOMWINDOW_CLOSED_TOPIC:
-        this._unregisterWindow(subject);
-        break;
       case TELEMETRY_SUBSESSIONSPLIT_TOPIC:
         this.afterSubsessionSplit();
         break;
     }
   },
 
   handleEvent(event) {
     switch(event.type) {
       case "TabOpen":
         this._onTabOpen();
         break;
+      case "unload":
+        this._unregisterWindow(event.target);
+        break;
     }
   },
 
   /**
    * This gets called shortly after the SessionStore has finished restoring
    * windows and tabs. It counts the open tabs and adds listeners to all the
    * windows.
    */
   _setupAfterRestore() {
     // Make sure to catch new chrome windows and subsession splits.
     Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, false);
-    Services.obs.addObserver(this, DOMWINDOW_CLOSED_TOPIC, false);
     Services.obs.addObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
 
     // Attach the tabopen handlers to the existing Windows.
     let browserEnum = Services.wm.getEnumerator("navigator:browser");
     while (browserEnum.hasMoreElements()) {
       this._registerWindow(browserEnum.getNext());
     }
 
@@ -108,30 +170,38 @@ let BrowserUsageTelemetry = {
     Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
     Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
   },
 
   /**
    * Adds listeners to a single chrome window.
    */
   _registerWindow(win) {
+    win.addEventListener("unload", this);
     win.addEventListener("TabOpen", this, true);
+
+    // Don't include URI and domain counts when in private mode.
+    if (PrivateBrowsingUtils.isWindowPrivate(win)) {
+      return;
+    }
+    win.gBrowser.addTabsProgressListener(URICountListener);
   },
 
   /**
    * Removes listeners from a single chrome window.
    */
   _unregisterWindow(win) {
-    // Ignore non-browser windows.
-    if (!(win instanceof Ci.nsIDOMWindow) ||
-        win.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
+    win.removeEventListener("unload", this);
+    win.removeEventListener("TabOpen", this, true);
+
+    // Don't include URI and domain counts when in private mode.
+    if (PrivateBrowsingUtils.isWindowPrivate(win.defaultView)) {
       return;
     }
-
-    win.removeEventListener("TabOpen", this, true);
+    win.defaultView.gBrowser.removeTabsProgressListener(URICountListener);
   },
 
   /**
    * Updates the tab counts.
    * @param {Number} [newTabCount=0] The count of the opened tabs across all windows. This
    *        is computed manually if not provided.
    */
   _onTabOpen(tabCount = 0) {
--- a/browser/modules/test/browser_UsageTelemetry.js
+++ b/browser/modules/test/browser_UsageTelemetry.js
@@ -1,143 +1,268 @@
 "use strict";
 
 const MAX_CONCURRENT_TABS = "browser.engagement.max_concurrent_tab_count";
 const TAB_EVENT_COUNT = "browser.engagement.tab_open_event_count";
 const MAX_CONCURRENT_WINDOWS = "browser.engagement.max_concurrent_window_count";
 const WINDOW_OPEN_COUNT = "browser.engagement.window_open_event_count";
+const TOTAL_URI_COUNT = "browser.engagement.total_uri_count";
+const UNIQUE_DOMAINS_COUNT = "browser.engagement.unique_domains_count";
 
 const TELEMETRY_SUBSESSION_TOPIC = "internal-telemetry-after-subsession-split";
 
 /**
+ * Waits for the web progress listener associated with this tab to fire an
+ * onLocationChange for a non-error page.
+ *
+ * @param {xul:browser} browser
+ *        A xul:browser.
+ *
+ * @return {Promise}
+ * @resolves When navigating to a non-error page.
+ */
+function browserLocationChanged(browser) {
+  return new Promise(resolve => {
+    let wpl = {
+      onStateChange() {},
+      onSecurityChange() {},
+      onStatusChange() {},
+      onLocationChange(aWebProgress, aRequest, aURI, aFlags) {
+        if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
+          browser.webProgress.removeProgressListener(filter);
+          filter.removeProgressListener(wpl);
+          resolve();
+        }
+      },
+      QueryInterface: XPCOMUtils.generateQI([
+        Ci.nsIWebProgressListener,
+        Ci.nsIWebProgressListener2,
+      ]),
+    };
+    const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+                     .createInstance(Ci.nsIWebProgress);
+    filter.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
+    browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
+  });
+}
+
+/**
  * An helper that checks the value of a scalar if it's expected to be > 0,
  * otherwise makes sure that the scalar it's not reported.
  */
 let checkScalar = (scalars, scalarName, value, msg) => {
   if (value > 0) {
     is(scalars[scalarName], value, msg);
     return;
   }
   ok(!(scalarName in scalars), scalarName + " must not be reported.");
 };
 
 /**
  * Get a snapshot of the scalars and check them against the provided values.
  */
-let checkScalars = (maxTabs, tabOpenCount, maxWindows, windowsOpenCount) => {
+let checkScalars = (maxTabs, tabOpenCount, maxWindows, windowsOpenCount, totalURIs, domainCount) => {
   const scalars =
     Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   // Check the expected values. Scalars that are never set must not be reported.
   checkScalar(scalars, MAX_CONCURRENT_TABS, maxTabs,
               "The maximum tab count must match the expected value.");
   checkScalar(scalars, TAB_EVENT_COUNT, tabOpenCount,
               "The number of open tab event count must match the expected value.");
   checkScalar(scalars, MAX_CONCURRENT_WINDOWS, maxWindows,
               "The maximum window count must match the expected value.");
   checkScalar(scalars, WINDOW_OPEN_COUNT, windowsOpenCount,
               "The number of window open event count must match the expected value.");
+  checkScalar(scalars, TOTAL_URI_COUNT, totalURIs,
+              "The total URI count must match the expected value.");
+  checkScalar(scalars, UNIQUE_DOMAINS_COUNT, domainCount,
+              "The unique domains count must match the expected value.");
 };
 
 add_task(function* test_tabsAndWindows() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   let openedTabs = [];
   let expectedTabOpenCount = 0;
   let expectedWinOpenCount = 0;
   let expectedMaxTabs = 0;
   let expectedMaxWins = 0;
 
   // Add a new tab and check that the count is right.
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   expectedTabOpenCount = 1;
   expectedMaxTabs = 2;
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
 
   // Add two new tabs in the same window.
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   expectedTabOpenCount += 2;
   expectedMaxTabs += 2;
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
 
   // Add a new window and then some tabs in it. An empty new windows counts as a tab.
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   // The new window started with a new tab, so account for it.
   expectedTabOpenCount += 4;
   expectedWinOpenCount += 1;
   expectedMaxWins = 2;
   expectedMaxTabs += 4;
 
   // Remove a tab from the first window, the max shouldn't change.
   yield BrowserTestUtils.removeTab(openedTabs.pop());
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
 
   // Remove all the extra windows and tabs.
   for (let tab of openedTabs) {
     yield BrowserTestUtils.removeTab(tab);
   }
   yield BrowserTestUtils.closeWindow(win);
 
   // Make sure all the scalars still have the expected values.
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount);
+  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
 });
 
 add_task(function* test_subsessionSplit() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   // Add a new window (that will have 4 tabs).
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   let openedTabs = [];
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
-  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://www.example.com"));
 
   // Check that the scalars have the right values.
-  checkScalars(5 /*maxTabs*/, 4 /*tabOpen*/, 2 /*maxWins*/, 1 /*winOpen*/);
+  checkScalars(5 /*maxTabs*/, 4 /*tabOpen*/, 2 /*maxWins*/, 1 /*winOpen*/,
+               1 /* toalURIs */, 1 /* uniqueDomains */);
 
   // Remove a tab.
   yield BrowserTestUtils.removeTab(openedTabs.pop());
 
   // Simulate a subsession split by clearing the scalars (via |snapshotScalars|) and
   // notifying the subsession split topic.
   Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                      true /* clearScalars*/);
   Services.obs.notifyObservers(null, TELEMETRY_SUBSESSION_TOPIC, "");
 
   // After a subsession split, only the MAX_CONCURRENT_* scalars must be available
   // and have the correct value. No tabs or windows were opened so other scalars
   // must not be reported.
-  checkScalars(4 /*maxTabs*/, 0 /*tabOpen*/, 2 /*maxWins*/, 0 /*winOpen*/);
+  checkScalars(4 /*maxTabs*/, 0 /*tabOpen*/, 2 /*maxWins*/, 0 /*winOpen*/,
+               0 /* toalURIs */, 0 /* uniqueDomains */);
 
   // Remove all the extra windows and tabs.
   for (let tab of openedTabs) {
     yield BrowserTestUtils.removeTab(tab);
   }
   yield BrowserTestUtils.closeWindow(win);
 });
 
+add_task(function* test_URIAndDomainCounts() {
+  // Let's reset the counts.
+  Services.telemetry.clearScalars();
+
+  let checkCounts = (URICount, domainCount) => {
+    // Get a snapshot of the scalars and then clear them.
+    const scalars =
+      Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
+    checkScalar(scalars, TOTAL_URI_COUNT, URICount,
+                "The URI scalar must contain the expected value.");
+    checkScalar(scalars, UNIQUE_DOMAINS_COUNT, domainCount,
+                "The unique domains scalar must contain the expected value.");
+  };
+
+  // Check that about:blank doesn't get counted in the URI total.
+  let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+  checkCounts(0, 0);
+
+  // Open a different page and check the counts.
+  yield BrowserTestUtils.loadURI(firstTab.linkedBrowser, "http://example.com/");
+  yield BrowserTestUtils.browserLoaded(firstTab.linkedBrowser);
+  checkCounts(1, 1);
+
+  // Activating a different tab must not increase the URI count.
+  let secondTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+  yield BrowserTestUtils.switchTab(gBrowser, firstTab);
+  checkCounts(1, 1);
+  yield BrowserTestUtils.removeTab(secondTab);
+
+  // Open a new window and set the tab to a new address.
+  let newWin = yield BrowserTestUtils.openNewBrowserWindow();
+  yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://example.com/");
+  yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
+  checkCounts(2, 1);
+
+  // We should not count AJAX requests.
+  const XHR_URL = "http://example.com/r";
+  yield ContentTask.spawn(newWin.gBrowser.selectedBrowser, XHR_URL, function(url) {
+    return new Promise(resolve => {
+      var xhr = new content.window.XMLHttpRequest();
+      xhr.open("GET", url);
+      xhr.onload = () => resolve();
+      xhr.send();
+    });
+  });
+  checkCounts(2, 1);
+
+  // Check that we're counting page fragments.
+  let loadingStopped = browserLocationChanged(newWin.gBrowser.selectedBrowser);
+  yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://example.com/#2");
+  yield loadingStopped;
+  checkCounts(3, 1);
+
+  // Check test.domain.com and some.domain.com are only counted once unique.
+  yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://test1.example.com/");
+  yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
+  checkCounts(4, 1);
+
+  // Make sure that the unique domains counter is incrementing for a different domain.
+  yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "https://example.org/");
+  yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
+  checkCounts(5, 2);
+
+  // Check that we only account for top level loads (e.g. we don't count URIs from
+  // embedded iframes).
+  yield ContentTask.spawn(newWin.gBrowser.selectedBrowser, null, function* () {
+    let doc = content.document;
+    let iframe = doc.createElement("iframe");
+    let promiseIframeLoaded = ContentTaskUtils.waitForEvent(iframe, "load", false);
+    iframe.src = "https://example.org/test";
+    doc.body.insertBefore(iframe, doc.body.firstChild);
+    yield promiseIframeLoaded;
+  });
+  checkCounts(5, 2);
+
+  // Clean up.
+  yield BrowserTestUtils.removeTab(firstTab);
+  yield BrowserTestUtils.closeWindow(newWin);
+});
+
 add_task(function* test_privateMode() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   // Open a private window and load a website in it.
   let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/");
   yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
 
   // Check that tab and window count is recorded.
   const scalars =
     Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
+  ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs in private mode.");
+  ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains in private mode.");
   is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value.");
   is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value.");
 
   // Clean up.
   yield BrowserTestUtils.closeWindow(privateWin);
 });
--- a/build/annotationProcessors/AnnotationProcessor.java
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -52,36 +52,36 @@ public class AnnotationProcessor {
 
         headerFile.append(
                 "#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
                 "#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
                 "\n" +
                 "#include \"mozilla/jni/Refs.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
-                "namespace widget {\n" +
+                "namespace java {\n" +
                 "\n");
 
         implementationFile.append(
                 "#include \"GeneratedJNIWrappers.h\"\n" +
                 "#include \"mozilla/jni/Accessors.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
-                "namespace widget {\n" +
+                "namespace java {\n" +
                 "\n");
 
         nativesFile.append(
                 "#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
                 "#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
                 "\n" +
                 "#include \"GeneratedJNIWrappers.h\"\n" +
                 "#include \"mozilla/jni/Natives.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
-                "namespace widget {\n" +
+                "namespace java {\n" +
                 "\n");
 
         while (jarClassIterator.hasNext()) {
             generateClass(jarClassIterator.next());
         }
 
         implementationFile.append(
                 "} /* widget */\n" +
--- a/build/annotationProcessors/SDKProcessor.java
+++ b/build/annotationProcessors/SDKProcessor.java
@@ -72,27 +72,27 @@ public class SDKProcessor {
         StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
         headerFile.append(
                 "#ifndef " + generatedFilePrefix + "_h__\n" +
                 "#define " + generatedFilePrefix + "_h__\n" +
                 "\n" +
                 "#include \"mozilla/jni/Refs.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
-                "namespace widget {\n" +
+                "namespace java {\n" +
                 "namespace sdk {\n" +
                 "\n");
 
         StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
         implementationFile.append(
                 "#include \"" + generatedFilePrefix + ".h\"\n" +
                 "#include \"mozilla/jni/Accessors.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
-                "namespace widget {\n" +
+                "namespace java {\n" +
                 "namespace sdk {\n" +
                 "\n");
 
         // Used to track the calls to the various class-specific initialisation functions.
         ClassLoader loader = null;
         try {
             loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) },
                                                 SDKProcessor.class.getClassLoader());
--- a/build/annotationProcessors/utils/Utils.java
+++ b/build/annotationProcessors/utils/Utils.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko.annotationProc
 import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
 
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
 import java.util.HashMap;
 
 /**
  * A collection of utility methods used by CodeGenerator. Largely used for translating types.
  */
 public class Utils {
 
     // A collection of lookup tables to simplify the functions to follow...
@@ -91,16 +92,20 @@ public class Utils {
             // to C, generating the corresponding code using this Java program. Really?!
             return "mozilla::jni::Class::Param";
         }
 
         if (type.equals(Throwable.class)) {
             return "mozilla::jni::Throwable::Param";
         }
 
+        if (type.equals(ByteBuffer.class)) {
+            return "mozilla::jni::ByteBuffer::Param";
+        }
+
         return "mozilla::jni::Object::Param";
     }
 
     public static String getNativeReturnType(Class<?> type, AnnotationInfo info) {
         final String name = type.getName().replace('.', '/');
 
         if (NATIVE_TYPES.containsKey(name)) {
             return NATIVE_TYPES.get(name);
@@ -123,16 +128,20 @@ public class Utils {
             // to C, generating the corresponding code using this Java program. Really?!
             return "mozilla::jni::Class::LocalRef";
         }
 
         if (type.equals(Throwable.class)) {
             return "mozilla::jni::Throwable::LocalRef";
         }
 
+        if (type.equals(ByteBuffer.class)) {
+            return "mozilla::jni::ByteBuffer::LocalRef";
+        }
+
         return "mozilla::jni::Object::LocalRef";
     }
 
     /**
      * Get the JNI class descriptor corresponding to the provided type parameter.
      *
      * @param type Class to determine the corresponding JNI descriptor for.
      * @return Class descripor as a String
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   doc_pseudo_elements.html
   doc_script_animation.html
   doc_simple_animation.html
   doc_multiple_animation_types.html
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor-registry.js
   !/devtools/client/shared/test/test-actor.js
 
 [browser_animation_animated_properties_displayed.js]
 [browser_animation_click_selects_animation.js]
 [browser_animation_controller_exposes_document_currentTime.js]
 skip-if = os == "linux" && !debug # Bug 1234567
 [browser_animation_empty_on_invalid_nodes.js]
--- a/devtools/client/canvasdebugger/canvasdebugger.js
+++ b/devtools/client/canvasdebugger/canvasdebugger.js
@@ -123,16 +123,22 @@ function shutdownCanvasDebugger() {
 /**
  * Functions handling target-related lifetime events.
  */
 var EventsHandler = {
   /**
    * Listen for events emitted by the current tab target.
    */
   initialize: function () {
+    // Make sure the backend is prepared to handle <canvas> contexts.
+    // Since actors are created lazily on the first request to them, we need to send an
+    // early request to ensure the CallWatcherActor is running and watching for new window
+    // globals.
+    gFront.setup({ reload: false });
+
     this._onTabNavigated = this._onTabNavigated.bind(this);
     gTarget.on("will-navigate", this._onTabNavigated);
     gTarget.on("navigate", this._onTabNavigated);
   },
 
   /**
    * Remove events emitted by the current tab target.
    */
@@ -143,18 +149,16 @@ var EventsHandler = {
 
   /**
    * Called for each location change in the debugged tab.
    */
   _onTabNavigated: function (event) {
     if (event != "will-navigate") {
       return;
     }
-    // Make sure the backend is prepared to handle <canvas> contexts.
-    gFront.setup({ reload: false });
 
     // Reset UI.
     SnapshotsListView.empty();
     CallsListView.empty();
 
     $("#record-snapshot").removeAttribute("checked");
     $("#record-snapshot").removeAttribute("disabled");
     $("#record-snapshot").hidden = false;
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -153,16 +153,17 @@ function once(aTarget, aEventName, aUseC
 
   for (let [add, remove] of [
     ["on", "off"], // Use event emitter before DOM events for consistency
     ["addEventListener", "removeEventListener"],
     ["addListener", "removeListener"]
   ]) {
     if ((add in aTarget) && (remove in aTarget)) {
       aTarget[add](aEventName, function onEvent(...aArgs) {
+        info("Got event: '" + aEventName + "' on " + aTarget + ".");
         aTarget[remove](aEventName, onEvent, aUseCapture);
         deferred.resolve(...aArgs);
       }, aUseCapture);
       break;
     }
   }
 
   return deferred.promise;
--- a/devtools/client/commandline/test/browser.ini
+++ b/devtools/client/commandline/test/browser.ini
@@ -53,16 +53,17 @@ support-files =
  browser_cmd_csscoverage_sheetA.css
  browser_cmd_csscoverage_sheetB.css
  browser_cmd_csscoverage_sheetC.css
  browser_cmd_csscoverage_sheetD.css
 [browser_cmd_folder.js]
 skip-if = (e10s && debug) # Bug 1034511 (docShell leaks on debug)
 [browser_cmd_highlight_01.js]
 [browser_cmd_highlight_02.js]
+skip-if = false
 [browser_cmd_highlight_03.js]
 [browser_cmd_inject.js]
 support-files =
  browser_cmd_inject.html
 [browser_cmd_csscoverage_util.js]
 skip-if = (e10s && debug) # Bug 1034511 (docShell leaks on debug)
 [browser_cmd_jsb.js]
 support-files =
--- a/devtools/client/commandline/test/browser_cmd_appcache_valid.js
+++ b/devtools/client/commandline/test/browser_cmd_appcache_valid.js
@@ -7,23 +7,23 @@ const TEST_URI = "http://sub1.test2.exam
                  "commandline/test/browser_cmd_appcache_valid_index.html";
 
 function test() {
   return Task.spawn(spawnTest).then(finish, helpers.handleError);
 }
 
 function* spawnTest() {
   let options = yield helpers.openTab(TEST_URI);
-  yield helpers.openToolbar(options);
 
   info("adding cache listener.");
-
   // Wait for site to be cached.
   yield helpers.listenOnce(gBrowser.contentWindow.applicationCache, "cached");
 
+  yield helpers.openToolbar(options);
+
   // Pages containing an appcache the notification bar gives options to allow
   // or deny permission for the app to save data offline. Let's click Allow.
   let notificationID = "offline-app-requested-sub1.test2.example.com";
   let notification = PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
 
   if (notification) {
     info("Authorizing offline storage.");
     notification.mainAction.callback();
--- a/devtools/client/commandline/test/browser_cmd_highlight_02.js
+++ b/devtools/client/commandline/test/browser_cmd_highlight_02.js
@@ -9,36 +9,37 @@ const TEST_PAGE = "data:text/html;charse
 
 function test() {
   return Task.spawn(function* () {
     let options = yield helpers.openTab(TEST_PAGE);
     yield helpers.openToolbar(options);
 
     info("highlighting the body node");
     yield runCommand("highlight body", options);
-    is(getHighlighterNumber(), 1, "The highlighter element exists for body");
+    is(yield getHighlighterNumber(), 1, "The highlighter element exists for body");
 
     info("highlighting the div node");
     yield runCommand("highlight div", options);
-    is(getHighlighterNumber(), 1, "The highlighter element exists for div");
+    is(yield getHighlighterNumber(), 1, "The highlighter element exists for div");
 
     info("highlighting the body node again, asking to keep the div");
     yield runCommand("highlight body --keep", options);
-    is(getHighlighterNumber(), 2, "2 highlighter elements have been created");
+    is(yield getHighlighterNumber(), 2, "2 highlighter elements have been created");
 
     info("unhighlighting all nodes");
     yield runCommand("unhighlight", options);
-    is(getHighlighterNumber(), 0, "All highlighters have been removed");
+    is(yield getHighlighterNumber(), 0, "All highlighters have been removed");
 
     yield helpers.closeToolbar(options);
     yield helpers.closeTab(options);
   }).then(finish, helpers.handleError);
 }
 
 function getHighlighterNumber() {
-  // Note that this only works as long as gcli tests aren't run with e10s on.
-  // To make this e10s ready, execute this in a content frame script instead.
-  return require("devtools/shared/gcli/commands/highlight").highlighters.length;
+  return ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+    const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    return require("devtools/shared/gcli/commands/highlight").highlighters.length;
+  });
 }
 
 function* runCommand(cmd, options) {
   yield helpers.audit(options, [{ setup: cmd, exec: {} }]);
 }
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -289,17 +289,17 @@ skip-if = (os == 'mac' && e10s && debug)
 [browser_dbg_interrupts.js]
 skip-if = e10s && debug
 [browser_dbg_listaddons.js]
 skip-if = e10s && debug
 tags = addons
 [browser_dbg_listtabs-01.js]
 skip-if = e10s # TODO
 [browser_dbg_listtabs-02.js]
-skip-if = e10s # TODO
+skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
 [browser_dbg_listtabs-03.js]
 skip-if = e10s && debug
 [browser_dbg_listworkers.js]
 [browser_dbg_location-changes-01-simple.js]
 skip-if = e10s && debug
 [browser_dbg_location-changes-02-blank.js]
 skip-if = e10s && debug
 [browser_dbg_location-changes-03-new.js]
--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
@@ -34,29 +34,27 @@ function test() {
       gClient.request({ to: globalActor, type: "ping" }, aResponse => {
         is(aResponse.pong, "pong", "Actor should respond to requests.");
 
         // Send another ping to see if the same actor is used.
         gClient.request({ to: globalActor, type: "ping" }, aResponse => {
           is(aResponse.pong, "pong", "Actor should respond to requests.");
 
           // Make sure that lazily-created actors are created only once.
-          let conn = transport._serverConnection;
-
-          // First we look for the pool of global actors.
-          let extraPools = conn._extraPools;
-          let globalPool;
+          let count = 0;
+          for (let connID of Object.getOwnPropertyNames(DebuggerServer._connections)) {
+            let conn = DebuggerServer._connections[connID];
+            let actorPrefix = conn._prefix + "test_one";
+            for (let pool of conn._extraPools) {
+              count += Object.keys(pool._actors).filter(e => {
+                return e.startsWith(actorPrefix);
+              }).length;
+            }
+          }
 
-          let actorPrefix = conn._prefix + "test_one";
-          let count = 0;
-          for (let pool of extraPools) {
-            count += Object.keys(pool._actors).filter(e => {
-              return e.startsWith(actorPrefix);
-            }).length;
-          }
           is(count, 2,
             "Only two actor exists in all pools. One tab actor and one global.");
 
           gClient.close(finish);
         });
       });
     });
   });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
@@ -75,17 +75,18 @@ function testAttachRemovedTab() {
     let deferred = promise.defer();
 
     gClient.addListener("paused", (aEvent, aPacket) => {
       ok(false, "Attaching to an exited tab actor shouldn't generate a pause.");
       deferred.reject();
     });
 
     gClient.request({ to: gTab2Actor, type: "attach" }, aResponse => {
-      is(aResponse.type, "exited", "Tab should consider itself exited.");
+      is(aResponse.error, "connectionClosed",
+         "Connection is gone since the tab was removed.");
       deferred.resolve();
     });
 
     return deferred.promise;
   });
 }
 
 function closeConnection() {
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -484,8 +484,13 @@ function pushPref(preferenceName, value)
  * @param {String} path
  *        Dotted path to use to expand the object.
  * @return {?} anything that is found at the provided path in the object.
  */
 function lookupPath(obj, path) {
   let segments = path.split(".");
   return segments.reduce((prev, current) => prev[current], obj);
 }
+
+var closeToolbox = Task.async(function* () {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  yield gDevTools.closeToolbox(target);
+});
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -6,18 +6,19 @@ support-files =
   doc_media_queries.html
   doc_pseudoelement.html
   doc_sourcemaps.css
   doc_sourcemaps.css.map
   doc_sourcemaps.html
   doc_sourcemaps.scss
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_computed_browser-styles.js]
 [browser_computed_cycle_color.js]
 [browser_computed_getNodeInfo.js]
 [browser_computed_keybindings_01.js]
 [browser_computed_keybindings_02.js]
--- a/devtools/client/inspector/fonts/test/browser.ini
+++ b/devtools/client/inspector/fonts/test/browser.ini
@@ -3,17 +3,18 @@ tags = devtools
 subsuite = devtools
 support-files =
   browser_fontinspector.html
   test_iframe.html
   ostrich-black.ttf
   ostrich-regular.ttf
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_fontinspector.js]
 [browser_fontinspector_edit-previews.js]
 [browser_fontinspector_edit-previews-show-all.js]
 [browser_fontinspector_theme-change.js]
--- a/devtools/client/inspector/layout/test/browser.ini
+++ b/devtools/client/inspector/layout/test/browser.ini
@@ -1,18 +1,19 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   doc_layout_iframe1.html
   doc_layout_iframe2.html
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_layout.js]
 [browser_layout_editablemodel.js]
 # [browser_layout_editablemodel_allproperties.js]
 # Disabled for too many intermittent failures (bug 1009322)
 [browser_layout_editablemodel_bluronclick.js]
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -42,18 +42,19 @@ support-files =
   lib_jquery_1.2_min.js
   lib_jquery_1.3_min.js
   lib_jquery_1.4_min.js
   lib_jquery_1.6_min.js
   lib_jquery_1.7_min.js
   lib_jquery_1.11.1_min.js
   lib_jquery_2.1.1_min.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_markup_accessibility_focus_blur.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation_after_edit.js]
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -29,18 +29,19 @@ support-files =
   doc_sourcemaps.html
   doc_sourcemaps.scss
   doc_style_editor_link.css
   doc_test_image.png
   doc_urls_clickable.css
   doc_urls_clickable.html
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_rules_add-property-and-reselect.js]
 [browser_rules_add-property-cancel_01.js]
 [browser_rules_add-property-cancel_02.js]
 [browser_rules_add-property-cancel_03.js]
 [browser_rules_add-property-commented.js]
--- a/devtools/client/inspector/shared/test/browser.ini
+++ b/devtools/client/inspector/shared/test/browser.ini
@@ -8,18 +8,19 @@ support-files =
   doc_content_stylesheet_imported.css
   doc_content_stylesheet_imported2.css
   doc_content_stylesheet_linked.css
   doc_content_stylesheet_script.css
   doc_content_stylesheet_xul.css
   doc_frame_script.js
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_styleinspector_context-menu-copy-color_01.js]
 [browser_styleinspector_context-menu-copy-color_02.js]
 subsuite = clipboard
 [browser_styleinspector_context-menu-copy-urls.js]
 subsuite = clipboard
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -26,18 +26,18 @@ support-files =
   doc_inspector_remove-iframe-during-load.html
   doc_inspector_search.html
   doc_inspector_search-reserved.html
   doc_inspector_search-suggestions.html
   doc_inspector_search-svg.html
   doc_inspector_select-last-selected-01.html
   doc_inspector_select-last-selected-02.html
   head.js
+  shared-head.js
   !/devtools/client/commandline/test/helpers.js
-  !/devtools/client/inspector/test/head.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_inspector_addNode_01.js]
 [browser_inspector_addNode_02.js]
 [browser_inspector_addNode_03.js]
 [browser_inspector_breadcrumbs.js]
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -1,17 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../framework/test/shared-head.js */
 /* import-globals-from ../../commandline/test/helpers.js */
 /* import-globals-from ../../shared/test/test-actor-registry.js */
-/* globals registerTestActor, getTestActor */
+/* import-globals-from ../../inspector/test/shared-head.js */
 "use strict";
 
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
@@ -24,16 +24,21 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
   this);
 
 // Import helpers registering the test-actor in remote targets
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
   this);
 
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this);
+
 DevToolsUtils.testing = true;
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 });
@@ -117,34 +122,16 @@ var startPicker = Task.async(function* (
  * node
  */
 function selectAndHighlightNode(selector, inspector) {
   info("Highlighting and selecting the node " + selector);
   return selectNode(selector, inspector, "test-highlight");
 }
 
 /**
- * Set the inspector's current selection to the first match of the given css
- * selector
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @return {Promise} Resolves when the inspector is updated with the new node
- */
-var selectNode = Task.async(function* (selector, inspector, reason = "test") {
-  info("Selecting the node for '" + selector + "'");
-  let nodeFront = yield getNodeFront(selector, inspector);
-  let updated = inspector.once("inspector-updated");
-  inspector.selection.setNodeFront(nodeFront, reason);
-  yield updated;
-});
-
-/**
  * Select node for a given selector, make it focusable and set focus in its
  * container element.
  * @param {String|NodeFront} selector
  * @param {InspectorPanel} inspector The current inspector-panel instance.
  * @return {MarkupContainer}
  */
 function* focusNode(selector, inspector) {
   getContainerForNodeFront(inspector.walker.rootNode, inspector).elt.focus();
@@ -177,42 +164,16 @@ function clearCurrentNodeSelection(inspe
  *         with an object: { tab, toolbox, inspector }.
  */
 var openInspectorForURL = Task.async(function* (url, hostType) {
   let tab = yield addTab(url);
   let { inspector, toolbox, testActor } = yield openInspector(hostType);
   return { tab, inspector, toolbox, testActor };
 });
 
-/**
- * Open the toolbox, with the inspector tool visible.
- * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
- * @return a promise that resolves when the inspector is ready
- */
-var openInspector = Task.async(function* (hostType) {
-  info("Opening the inspector");
-
-  let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
-                                        hostType);
-  let inspector = toolbox.getPanel("inspector");
-
-  if (inspector._updateProgress) {
-    info("Need to wait for the inspector to update");
-    yield inspector.once("inspector-updated");
-  }
-
-  info("Waiting for actor features to be detected");
-  yield inspector._detectingActorFeatures;
-
-  yield registerTestActor(toolbox.target.client);
-  let testActor = yield getTestActor(toolbox);
-
-  return {toolbox, inspector, testActor};
-});
-
 function getActiveInspector() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.getToolbox(target).getPanel("inspector");
 }
 
 /**
  * Right click on a node in the test page and click on the inspect menu item.
  * @param {TestActor}
@@ -240,113 +201,16 @@ var clickOnInspectMenuItem = Task.async(
   let contextClosed = once(contentAreaContextMenu, "popuphidden");
   contentAreaContextMenu.hidePopup();
   yield contextClosed;
 
   return getActiveInspector();
 });
 
 /**
- * Open the toolbox, with the inspector tool visible, and the one of the sidebar
- * tabs selected.
- *
- * @param {String} id
- *        The ID of the sidebar tab to be opened
- * @return a promise that resolves when the inspector is ready and the tab is
- * visible and ready
- */
-var openInspectorSidebarTab = Task.async(function* (id) {
-  let {toolbox, inspector, testActor} = yield openInspector();
-
-  info("Selecting the " + id + " sidebar");
-  inspector.sidebar.select(id);
-
-  return {
-    toolbox,
-    inspector,
-    testActor
-  };
-});
-
-/**
- * Open the toolbox, with the inspector tool visible, and the rule-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the rule view
- * is visible and ready
- */
-function openRuleView() {
-  return openInspectorSidebarTab("ruleview").then(data => {
-    return {
-      toolbox: data.toolbox,
-      inspector: data.inspector,
-      testActor: data.testActor,
-      view: data.inspector.ruleview.view
-    };
-  });
-}
-
-/**
- * Open the toolbox, with the inspector tool visible, and the computed-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the computed
- * view is visible and ready
- */
-function openComputedView() {
-  return openInspectorSidebarTab("computedview").then(data => {
-    return {
-      toolbox: data.toolbox,
-      inspector: data.inspector,
-      testActor: data.testActor,
-      view: data.inspector.computedview.computedView
-    };
-  });
-}
-
-/**
- * Select the rule view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- *        The opened inspector panel
- * @return {CssRuleView} the rule view
- */
-function selectRuleView(inspector) {
-  inspector.sidebar.select("ruleview");
-  return inspector.ruleview.view;
-}
-
-/**
- * Select the computed view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- *        The opened inspector panel
- * @return {CssComputedView} the computed view
- */
-function selectComputedView(inspector) {
-  inspector.sidebar.select("computedview");
-  return inspector.computedview.computedView;
-}
-
-/**
- * Get the NodeFront for a node that matches a given css selector, via the
- * protocol.
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @return {Promise} Resolves to the NodeFront instance
- */
-function getNodeFront(selector, {walker}) {
-  if (selector._form) {
-    return selector;
-  }
-  return walker.querySelector(walker.rootNode, selector);
-}
-
-/**
  * Get the NodeFront for a node that matches a given css selector inside a
  * given iframe.
  * @param {String|NodeFront} selector
  * @param {String|NodeFront} frameSelector A selector that matches the iframe
  * the node is in
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
  * loaded in the toolbox
  * @return {Promise} Resolves when the inspector is updated with the new node
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/shared-head.js
@@ -0,0 +1,152 @@
+/* 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/. */
+
+"use strict";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* globals registerTestActor, getTestActor, Task, openToolboxForTab, gBrowser */
+
+// This file contains functions related to the inspector that are also of interest to
+// other test directores as well.
+
+/**
+ * Open the toolbox, with the inspector tool visible.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return a promise that resolves when the inspector is ready
+ */
+var openInspector = Task.async(function* (hostType) {
+  info("Opening the inspector");
+
+  let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
+                                        hostType);
+  let inspector = toolbox.getPanel("inspector");
+
+  if (inspector._updateProgress) {
+    info("Need to wait for the inspector to update");
+    yield inspector.once("inspector-updated");
+  }
+
+  info("Waiting for actor features to be detected");
+  yield inspector._detectingActorFeatures;
+
+  yield registerTestActor(toolbox.target.client);
+  let testActor = yield getTestActor(toolbox);
+
+  return {toolbox, inspector, testActor};
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the one of the sidebar
+ * tabs selected.
+ *
+ * @param {String} id
+ *        The ID of the sidebar tab to be opened
+ * @return a promise that resolves when the inspector is ready and the tab is
+ * visible and ready
+ */
+var openInspectorSidebarTab = Task.async(function* (id) {
+  let {toolbox, inspector, testActor} = yield openInspector();
+
+  info("Selecting the " + id + " sidebar");
+  inspector.sidebar.select(id);
+
+  return {
+    toolbox,
+    inspector,
+    testActor
+  };
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the rule-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the rule view
+ * is visible and ready
+ */
+function openRuleView() {
+  return openInspectorSidebarTab("ruleview").then(data => {
+    return {
+      toolbox: data.toolbox,
+      inspector: data.inspector,
+      testActor: data.testActor,
+      view: data.inspector.ruleview.view
+    };
+  });
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the computed-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the computed
+ * view is visible and ready
+ */
+function openComputedView() {
+  return openInspectorSidebarTab("computedview").then(data => {
+    return {
+      toolbox: data.toolbox,
+      inspector: data.inspector,
+      testActor: data.testActor,
+      view: data.inspector.computedview.computedView
+    };
+  });
+}
+
+/**
+ * Select the rule view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ *        The opened inspector panel
+ * @return {CssRuleView} the rule view
+ */
+function selectRuleView(inspector) {
+  inspector.sidebar.select("ruleview");
+  return inspector.ruleview.view;
+}
+
+/**
+ * Select the computed view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ *        The opened inspector panel
+ * @return {CssComputedView} the computed view
+ */
+function selectComputedView(inspector) {
+  inspector.sidebar.select("computedview");
+  return inspector.computedview.computedView;
+}
+
+/**
+ * Get the NodeFront for a node that matches a given css selector, via the
+ * protocol.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves to the NodeFront instance
+ */
+function getNodeFront(selector, {walker}) {
+  if (selector._form) {
+    return selector;
+  }
+  return walker.querySelector(walker.rootNode, selector);
+}
+
+/**
+ * Set the inspector's current selection to the first match of the given css
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @param {String} reason Defaults to "test" which instructs the inspector not
+ * to highlight the node upon selection
+ * @return {Promise} Resolves when the inspector is updated with the new node
+ */
+var selectNode = Task.async(function* (selector, inspector, reason = "test") {
+  info("Selecting the node for '" + selector + "'");
+  let nodeFront = yield getNodeFront(selector, inspector);
+  let updated = inspector.once("inspector-updated");
+  inspector.selection.setNodeFront(nodeFront, reason);
+  yield updated;
+});
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -88,22 +88,16 @@ function tunnelToInnerBrowser(outer, inn
       // Replace the outer browser's native messageManager with a message manager tunnel
       // which we can use to route messages of interest to the inner browser instead.
       // Note: The _actual_ messageManager accessible from
       // `browser.frameLoader.messageManager` is not overridable and is left unchanged.
       // Only the XBL getter `browser.messageManager` is overridden.  Browser UI code
       // always uses this getter instead of `browser.frameLoader.messageManager` directly,
       // so this has the effect of overriding the message manager for browser UI code.
       mmTunnel = new MessageManagerTunnel(outer, inner);
-      Object.defineProperty(outer, "messageManager", {
-        value: mmTunnel,
-        writable: false,
-        configurable: true,
-        enumerable: true,
-      });
 
       // We are tunneling to an inner browser with a specific remoteness, so it is simpler
       // for the logic of the browser UI to assume this tab has taken on that remoteness,
       // even though it's not true.  Since the actions the browser UI performs are sent
       // down to the inner browser by this tunnel, the tab's remoteness effectively is the
       // remoteness of the inner browser.
       Object.defineProperty(outer, "isRemoteBrowser", {
         get() {
@@ -206,17 +200,16 @@ function tunnelToInnerBrowser(outer, inn
 
       // Reset the XBL binding back to the default.
       outer.destroy();
       outer.style.MozBinding = "";
 
       // Reset overridden XBL properties and methods.  Deleting the override
       // means it will fallback to the original XBL binding definitions which
       // are on the prototype.
-      delete outer.messageManager;
       delete outer.isRemoteBrowser;
       delete outer.hasContentOpener;
       delete outer.docShellIsActive;
       delete outer.setDocShellIsActiveAndForeground;
 
       mmTunnel.destroy();
       mmTunnel = null;
 
@@ -263,44 +256,51 @@ function copyPermanentKey(outer, inner) 
  * without having to touch the original code paths that use them.
  */
 function MessageManagerTunnel(outer, inner) {
   if (outer.isRemoteBrowser) {
     throw new Error("The outer browser must be non-remote.");
   }
   this.outer = outer;
   this.inner = inner;
+  this.tunneledMessageNames = new Set();
   this.init();
 }
 
 MessageManagerTunnel.prototype = {
 
   /**
    * Most message manager methods are left alone and are just passed along to
-   * the outer browser's real message manager.  `sendAsyncMessage` is only one
-   * with special behavior.
+   * the outer browser's real message manager.
    */
   PASS_THROUGH_METHODS: [
-    "addMessageListener",
-    "loadFrameScript",
     "killChild",
     "assertPermission",
     "assertContainApp",
     "assertAppHasPermission",
     "assertAppHasStatus",
     "removeDelayedFrameScript",
     "getDelayedFrameScripts",
     "loadProcessScript",
     "removeDelayedProcessScript",
     "getDelayedProcessScripts",
-    "removeMessageListener",
     "addWeakMessageListener",
     "removeWeakMessageListener",
   ],
 
+  /**
+   * The following methods are overridden with special behavior while tunneling.
+   */
+  OVERRIDDEN_METHODS: [
+    "loadFrameScript",
+    "addMessageListener",
+    "removeMessageListener",
+    "sendAsyncMessage",
+  ],
+
   OUTER_TO_INNER_MESSAGES: [
     // Messages sent from remote-browser.xml
     "Browser:PurgeSessionHistory",
     "InPermitUnload",
     "PermitUnload",
     // Messages sent from browser.js
     "Browser:Reload",
     // Messages sent from SelectParentHelper.jsm
@@ -333,69 +333,197 @@ MessageManagerTunnel.prototype = {
     "PageVisibility:Hide",
     "PageVisibility:Show",
     // Messages sent to SessionStore.jsm
     "SessionStore:update",
     // Messages sent to BrowserTestUtils.jsm
     "browser-test-utils:loadEvent",
   ],
 
+  OUTER_TO_INNER_MESSAGE_PREFIXES: [
+    // Messages sent from DevTools
+    "debug:",
+  ],
+
+  INNER_TO_OUTER_MESSAGE_PREFIXES: [
+    // Messages sent to DevTools
+    "debug:",
+  ],
+
+  OUTER_TO_INNER_FRAME_SCRIPTS: [
+    // DevTools server for OOP frames
+    "resource://devtools/server/child.js"
+  ],
+
   get outerParentMM() {
+    if (!this.outer.frameLoader) {
+      return null;
+    }
     return this.outer.frameLoader.messageManager;
   },
 
   get outerChildMM() {
     // This is only possible because we require the outer browser to be
     // non-remote, so we're able to reach into its window and use the child
     // side message manager there.
     let docShell = this.outer.frameLoader.docShell;
     return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIContentFrameMessageManager);
   },
 
   get innerParentMM() {
+    if (!this.inner.frameLoader) {
+      return null;
+    }
     return this.inner.frameLoader.messageManager;
   },
 
+  init() {
+    for (let method of this.PASS_THROUGH_METHODS) {
+      // Workaround bug 449811 to ensure a fresh binding each time through the loop
+      let _method = method;
+      this[_method] = (...args) => {
+        if (!this.outerParentMM) {
+          return null;
+        }
+        return this.outerParentMM[_method](...args);
+      };
+    }
+
+    for (let name of this.INNER_TO_OUTER_MESSAGES) {
+      this.innerParentMM.addMessageListener(name, this);
+      this.tunneledMessageNames.add(name);
+    }
+
+    Services.obs.addObserver(this, "message-manager-close", false);
+
+    // Replace the outer browser's messageManager with this tunnel
+    Object.defineProperty(this.outer, "messageManager", {
+      value: this,
+      writable: false,
+      configurable: true,
+      enumerable: true,
+    });
+  },
+
+  destroy() {
+    if (this.destroyed) {
+      return;
+    }
+    this.destroyed = true;
+    debug("Destroy tunnel");
+
+    // Watch for the messageManager to close.  In most cases, the caller will stop the
+    // tunnel gracefully before this, but when the browser window closes or application
+    // exits, we may not see the high-level close events.
+    Services.obs.removeObserver(this, "message-manager-close");
+
+    // Reset the messageManager.  Deleting the override means it will fallback to the
+    // original XBL binding definitions which are on the prototype.
+    delete this.outer.messageManager;
+
+    for (let name of this.tunneledMessageNames) {
+      this.innerParentMM.removeMessageListener(name, this);
+    }
+
+    // Some objects may have cached this tunnel as the messageManager for a frame.  To
+    // ensure it keeps working after tunnel close, rewrite the overidden methods as pass
+    // through methods.
+    for (let method of this.OVERRIDDEN_METHODS) {
+      // Workaround bug 449811 to ensure a fresh binding each time through the loop
+      let _method = method;
+      this[_method] = (...args) => {
+        if (!this.outerParentMM) {
+          return null;
+        }
+        return this.outerParentMM[_method](...args);
+      };
+    }
+  },
+
+  observe(subject, topic, data) {
+    if (topic != "message-manager-close") {
+      return;
+    }
+    if (subject == this.innerParentMM) {
+      debug("Inner messageManager has closed");
+      this.destroy();
+    }
+    if (subject == this.outerParentMM) {
+      debug("Outer messageManager has closed");
+      this.destroy();
+    }
+  },
+
+  loadFrameScript(url, ...args) {
+    debug(`Calling loadFrameScript for ${url}`);
+
+    if (!this.OUTER_TO_INNER_FRAME_SCRIPTS.includes(url)) {
+      debug(`Should load ${url} into inner?`);
+      this.outerParentMM.loadFrameScript(url, ...args);
+      return;
+    }
+
+    debug(`Load ${url} into inner`);
+    this.innerParentMM.loadFrameScript(url, ...args);
+  },
+
+  addMessageListener(name, ...args) {
+    debug(`Calling addMessageListener for ${name}`);
+
+    debug(`Add outer listener for ${name}`);
+    // Add an outer listener, just like a simple pass through
+    this.outerParentMM.addMessageListener(name, ...args);
+
+    // If the message name is part of a prefix we're tunneling, we also need to add the
+    // tunnel as an inner listener.
+    if (this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix))) {
+      debug(`Add inner listener for ${name}`);
+      this.innerParentMM.addMessageListener(name, this);
+      this.tunneledMessageNames.add(name);
+    }
+  },
+
+  removeMessageListener(name, ...args) {
+    debug(`Calling removeMessageListener for ${name}`);
+
+    debug(`Remove outer listener for ${name}`);
+    // Remove an outer listener, just like a simple pass through
+    this.outerParentMM.removeMessageListener(name, ...args);
+
+    // Leave the tunnel as an inner listener for the case of prefix messages to avoid
+    // tracking counts of add calls.  The inner listener will get removed on destroy.
+  },
+
   sendAsyncMessage(name, ...args) {
     debug(`Calling sendAsyncMessage for ${name}`);
 
-    if (!this.OUTER_TO_INNER_MESSAGES.includes(name)) {
+    if (!this._shouldTunnelOuterToInner(name)) {
       debug(`Should ${name} go to inner?`);
       this.outerParentMM.sendAsyncMessage(name, ...args);
       return;
     }
 
     debug(`${name} outer -> inner`);
     this.innerParentMM.sendAsyncMessage(name, ...args);
   },
 
-  init() {
-    for (let method of this.PASS_THROUGH_METHODS) {
-      // Workaround bug 449811 to ensure a fresh binding each time through the loop
-      let _method = method;
-      this[_method] = (...args) => {
-        return this.outerParentMM[_method](...args);
-      };
-    }
-
-    for (let message of this.INNER_TO_OUTER_MESSAGES) {
-      this.innerParentMM.addMessageListener(message, this);
-    }
-  },
-
-  destroy() {
-    for (let message of this.INNER_TO_OUTER_MESSAGES) {
-      this.innerParentMM.removeMessageListener(message, this);
-    }
-  },
-
   receiveMessage({ name, data, objects, principal }) {
-    if (!this.INNER_TO_OUTER_MESSAGES.includes(name)) {
+    if (!this._shouldTunnelInnerToOuter(name)) {
       debug(`Received unexpected message ${name}`);
       return;
     }
 
     debug(`${name} inner -> outer`);
     this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
   },
 
+  _shouldTunnelOuterToInner(name) {
+    return this.OUTER_TO_INNER_MESSAGES.includes(name) ||
+           this.OUTER_TO_INNER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+  },
+
+  _shouldTunnelInnerToOuter(name) {
+    return this.INNER_TO_OUTER_MESSAGES.includes(name) ||
+           this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+  },
+
 };
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -257,42 +257,39 @@ ResponsiveUI.prototype = {
    * Open RDM while preserving the state of the page.  We use `swapFrameLoaders`
    * to ensure all in-page state is preserved, just like when you move a tab to
    * a new window.
    *
    * For more details, see /devtools/docs/responsive-design-mode.md.
    */
   init: Task.async(function* () {
     let ui = this;
-    let toolViewportContentBrowser;
 
-    // Watch for the tab's beforeunload to catch app shutdown early
-    this.tab.linkedBrowser.addEventListener("beforeunload", this, true);
+    // Watch for tab close so we can clean up RDM synchronously
+    this.tab.addEventListener("TabClose", this);
 
     // Swap page content from the current tab into a viewport within RDM
     this.swap = swapToInnerBrowser({
       tab: this.tab,
       containerURL: TOOL_URL,
       getInnerBrowser: Task.async(function* (containerBrowser) {
         let toolWindow = ui.toolWindow = containerBrowser.contentWindow;
         toolWindow.addEventListener("message", ui);
         yield message.request(toolWindow, "init");
         toolWindow.addInitialViewport("about:blank");
         yield message.wait(toolWindow, "browser-mounted");
-        toolViewportContentBrowser = ui.getViewportBrowser();
-        return toolViewportContentBrowser;
+        return ui.getViewportBrowser();
       })
     });
     yield this.swap.start();
 
     // Notify the inner browser to start the frame script
     yield message.request(this.toolWindow, "start-frame-script");
 
-    this.touchEventSimulator =
-      new TouchEventSimulator(toolViewportContentBrowser);
+    this.touchEventSimulator = new TouchEventSimulator(this.getViewportBrowser());
   }),
 
   /**
    * Close RDM and restore page content back into a regular tab.
    *
    * @param object
    *        Destroy options, which currently includes a `reason` string.
    * @return boolean
@@ -300,36 +297,36 @@ ResponsiveUI.prototype = {
    *         was already in progress.
    */
   destroy: Task.async(function* (options) {
     if (this.destroying) {
       return false;
     }
     this.destroying = true;
 
-    // If our tab is about to be unloaded, there's not enough time to exit
+    // If our tab is about to be closed, there's not enough time to exit
     // gracefully, but that shouldn't be a problem since the tab will go away.
-    // So, skip any yielding when we're about to unload.
-    let isBeforeUnload = options && options.reason == "beforeunload";
+    // So, skip any yielding when we're about to close the tab.
+    let isTabClosing = options && options.reason == "TabClose";
 
     // Ensure init has finished before starting destroy
-    if (!isBeforeUnload) {
+    if (!isTabClosing) {
       yield this.inited;
     }
 
-    this.tab.linkedBrowser.removeEventListener("beforeunload", this, true);
+    this.tab.removeEventListener("TabClose", this);
     this.toolWindow.removeEventListener("message", this);
 
     // Stop the touch event simulator if it was running
-    if (!isBeforeUnload) {
+    if (!isTabClosing) {
       yield this.touchEventSimulator.stop();
     }
 
     // Notify the inner browser to stop the frame script
-    if (!isBeforeUnload) {
+    if (!isTabClosing) {
       yield message.request(this.toolWindow, "stop-frame-script");
     }
 
     // Destroy local state
     let swap = this.swap;
     this.browserWindow = null;
     this.tab = null;
     this.inited = null;
@@ -347,29 +344,17 @@ ResponsiveUI.prototype = {
 
   handleEvent(event) {
     let { browserWindow, tab } = this;
 
     switch (event.type) {
       case "message":
         this.handleMessage(event);
         break;
-      case "beforeunload":
-        // Ignore the event for locations like about:blank
-        if (event.target.location != TOOL_URL) {
-          return;
-        }
-        // We want to ensure we close RDM before browser window close when
-        // quitting.  Session restore starts its final session data flush when
-        // it gets the `quit-application-granted` notification.  If we were to
-        // wait for browser windows to close before destroying (which will undo
-        // the content swap), session restore gets wedged in a confused state
-        // since we're moving browsers at the same time that it is blocking
-        // shutdown on messages from each browser.  So instead, we destroy
-        // earlier based on the tab's `beforeunload` event.
+      case "TabClose":
         ResponsiveUIManager.closeIfNeeded(browserWindow, tab, {
           reason: event.type,
         });
         break;
     }
   },
 
   handleMessage(event) {
@@ -425,11 +410,18 @@ ResponsiveUI.prototype = {
 
   /**
    * Helper for tests/reloading the viewport. Assumes a single viewport for now.
    */
   getViewportBrowser() {
     return this.toolWindow.getViewportBrowser();
   },
 
+  /**
+   * Helper for contacting the viewport content. Assumes a single viewport for now.
+   */
+  getViewportMessageManager() {
+    return this.getViewportBrowser().messageManager;
+  },
+
 };
 
 EventEmitter.decorate(ResponsiveUI.prototype);
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -5,26 +5,31 @@ subsuite = devtools
 skip-if = !e10s
 support-files =
   devices.json
   doc_page_state.html
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
+  !/devtools/client/inspector/test/shared-head.js
+  !/devtools/client/shared/test/test-actor.js
+  !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_device_modal_error.js]
 [browser_device_modal_exit.js]
 [browser_device_modal_submit.js]
 [browser_device_width.js]
 [browser_exit_button.js]
 [browser_frame_script_active.js]
 [browser_menu_item_01.js]
 [browser_menu_item_02.js]
 [browser_mouse_resize.js]
 [browser_navigation.js]
 [browser_page_state.js]
 [browser_resize_cmd.js]
-skip-if = true # GCLI target confused after swap, will fix in bug 1240907
+skip-if = true # GCLI target confused after swap, will fix in bug 1240912
 [browser_screenshot_button.js]
 [browser_shutdown_close_sync.js]
+[browser_toolbox_computed_view.js]
+[browser_toolbox_rule_view.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
--- a/devtools/client/responsive.html/test/browser/browser_shutdown_close_sync.js
+++ b/devtools/client/responsive.html/test/browser/browser_shutdown_close_sync.js
@@ -1,24 +1,38 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Verify RDM closes synchronously in the case of the "beforeunload" event.
+// Verify RDM closes synchronously when tabs are closed.
 
 const TEST_URL = "http://example.com/";
+
 add_task(function* () {
   let tab = yield addTab(TEST_URL);
 
   let { ui } = yield openRDM(tab);
 
   closeRDM(tab, {
-    reason: "beforeunload",
+    reason: "TabClose",
   });
 
   // This flag is set at the end of `ResponsiveUI.destroy`.  If it is true
   // without yielding on `closeRDM` above, then we must have closed
   // synchronously.
   is(ui.destroyed, true, "RDM closed synchronously");
 
   yield removeTab(tab);
 });
+
+add_task(function* () {
+  let tab = yield addTab(TEST_URL);
+
+  let { ui } = yield openRDM(tab);
+
+  yield removeTab(tab);
+
+  // This flag is set at the end of `ResponsiveUI.destroy`.  If it is true without
+  // yielding on `closeRDM` itself and only removing the tab, then we must have closed
+  // synchronously in response to tab closing.
+  is(ui.destroyed, true, "RDM closed synchronously");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the computed-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+                 "div {" +
+                 "  width: 500px;" +
+                 "  height: 10px;" +
+                 "  background: purple;" +
+                 "} " +
+                 "@media screen and (max-width: 200px) {" +
+                 "  div { " +
+                 "    width: 100px;" +
+                 "  }" +
+                 "};" +
+                 "</style><div></div></html>";
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+  info("Open the responsive design mode and set its size to 500x500 to start");
+  yield setViewportSize(ui, manager, 500, 500);
+
+  info("Open the inspector, computed-view and select the test node");
+  let { inspector, view } = yield openComputedView();
+  yield selectNode("div", inspector);
+
+  info("Try shrinking the viewport and checking the applied styles");
+  yield testShrink(view, inspector, ui, manager);
+
+  info("Try growing the viewport and checking the applied styles");
+  yield testGrow(view, inspector, ui, manager);
+
+  yield closeToolbox();
+});
+
+function* testShrink(computedView, inspector, ui, manager) {
+  is(computedWidth(computedView), "500px", "Should show 500px initially.");
+
+  let onRefresh = inspector.once("computed-view-refreshed");
+  yield setViewportSize(ui, manager, 100, 100);
+  yield onRefresh;
+
+  is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
+}
+
+function* testGrow(computedView, inspector, ui, manager) {
+  let onRefresh = inspector.once("computed-view-refreshed");
+  yield setViewportSize(ui, manager, 500, 500);
+  yield onRefresh;
+
+  is(computedWidth(computedView), "500px", "Should be 500px after growing.");
+}
+
+function computedWidth(computedView) {
+  for (let prop of computedView.propertyViews) {
+    if (prop.name === "width") {
+      return prop.valueNode.textContent;
+    }
+  }
+  return null;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the rule-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+                 "div {" +
+                 "  width: 500px;" +
+                 "  height: 10px;" +
+                 "  background: purple;" +
+                 "} " +
+                 "@media screen and (max-width: 200px) {" +
+                 "  div { " +
+                 "    width: 100px;" +
+                 "  }" +
+                 "};" +
+                 "</style><div></div></html>";
+
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+});
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+  info("Open the responsive design mode and set its size to 500x500 to start");
+  yield setViewportSize(ui, manager, 500, 500);
+
+  info("Open the inspector, rule-view and select the test node");
+  let { inspector, view } = yield openRuleView();
+  yield selectNode("div", inspector);
+
+  info("Try shrinking the viewport and checking the applied styles");
+  yield testShrink(view, ui, manager);
+
+  info("Try growing the viewport and checking the applied styles");
+  yield testGrow(view, ui, manager);
+
+  info("Check that ESC still opens the split console");
+  yield testEscapeOpensSplitConsole(inspector);
+
+  yield closeToolbox();
+});
+
+function* testShrink(ruleView, ui, manager) {
+  is(numberOfRules(ruleView), 2, "Should have two rules initially.");
+
+  info("Resize to 100x100 and wait for the rule-view to update");
+  let onRefresh = ruleView.once("ruleview-refreshed");
+  yield setViewportSize(ui, manager, 100, 100);
+  yield onRefresh;
+
+  is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
+}
+
+function* testGrow(ruleView, ui, manager) {
+  info("Resize to 500x500 and wait for the rule-view to update");
+  let onRefresh = ruleView.once("ruleview-refreshed");
+  yield setViewportSize(ui, manager, 500, 500);
+  yield onRefresh;
+
+  is(numberOfRules(ruleView), 2, "Should have two rules after growing.");
+}
+
+function* testEscapeOpensSplitConsole(inspector) {
+  ok(!inspector._toolbox._splitConsole, "Console is not split.");
+
+  info("Press escape");
+  let onSplit = inspector._toolbox.once("split-console");
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  yield onSplit;
+
+  ok(inspector._toolbox._splitConsole, "Console is split after pressing ESC.");
+}
+
+function numberOfRules(ruleView) {
+  return ruleView.element.querySelectorAll(".ruleview-code").length;
+}
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -2,51 +2,66 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../../framework/test/shared-head.js */
 /* import-globals-from ../../../framework/test/shared-redux-head.js */
 /* import-globals-from ../../../commandline/test/helpers.js */
+/* import-globals-from ../../../inspector/test/shared-head.js */
 
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
   this);
 
 // Import the GCLI test helper
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
   this);
 
+// Import helpers registering the test-actor in remote targets
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
+  this);
+
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this);
+
 const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
+const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
+
+const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
+const { getOwnerWindow } = require("sdk/tabs/utils");
+const asyncStorage = require("devtools/shared/async-storage");
 
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
 DevToolsUtils.testing = true;
 Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
 Services.prefs.setCharPref("devtools.devices.url",
   TEST_URI_ROOT + "devices.json");
 Services.prefs.setBoolPref("devtools.responsive.html.enabled", true);
 
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.html.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
+  asyncStorage.removeItem("devtools.devices.url_cache");
 });
+
+// This depends on the "devtools.responsive.html.enabled" pref
 const { ResponsiveUIManager } = require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
-const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
-const { getOwnerWindow } = require("sdk/tabs/utils");
-
-const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
 
 /**
  * Open responsive design mode for the given tab.
  */
 var openRDM = Task.async(function* (tab) {
   info("Opening responsive design mode");
   let manager = ResponsiveUIManager;
   let ui = yield manager.openIfNeeded(getOwnerWindow(tab), tab);
--- a/devtools/client/shared/test/test-actor-registry.js
+++ b/devtools/client/shared/test/test-actor-registry.js
@@ -1,105 +1,97 @@
 /* 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/. */
 "use strict";
 
 (function (exports) {
-
-  var Cu = Components.utils;
-  var Ci = Components.interfaces;
-  var Cc = Components.classes;
-  var CC = Components.Constructor;
+  const Cu = Components.utils;
+  const CC = Components.Constructor;
 
-  var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-  var { fetch } = require("devtools/shared/DevToolsUtils");
-  var promise = require("promise");
-  var defer = require("devtools/shared/defer");
+  const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  const { fetch } = require("devtools/shared/DevToolsUtils");
+  const defer = require("devtools/shared/defer");
+  const { Task } = require("devtools/shared/task");
 
-  var TEST_URL_ROOT = "http://example.com/browser/devtools/client/shared/test/";
-  var ACTOR_URL = TEST_URL_ROOT + "test-actor.js";
+  const TEST_URL_ROOT = "http://example.com/browser/devtools/client/shared/test/";
+  const ACTOR_URL = TEST_URL_ROOT + "test-actor.js";
 
-// Register a test actor that can operate on the remote document
+  // Register a test actor that can operate on the remote document
   exports.registerTestActor = Task.async(function* (client) {
-  // First, instanciate ActorRegistryFront to be able to dynamically
-  // register an actor
+    // First, instanciate ActorRegistryFront to be able to dynamically register an actor
     let deferred = defer();
     client.listTabs(deferred.resolve);
     let response = yield deferred.promise;
     let { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
     let registryFront = ActorRegistryFront(client, response);
 
-  // Then ask to register our test-actor to retrieve its front
+    // Then ask to register our test-actor to retrieve its front
     let options = {
       type: { tab: true },
       constructor: "TestActor",
       prefix: "testActor"
     };
     let testActorFront = yield registryFront.registerActor(ACTOR_URL, options);
     return testActorFront;
   });
 
-// Load the test actor in a custom sandbox
-// as we can't use SDK module loader with URIs
-  var loadFront = Task.async(function* () {
+  // Load the test actor in a custom sandbox as we can't use SDK module loader with URIs
+  let loadFront = Task.async(function* () {
     let sourceText = yield request(ACTOR_URL);
     const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();
     const sandbox = Cu.Sandbox(principal);
-    const exports = sandbox.exports = {};
+    sandbox.exports = {};
     sandbox.require = require;
     Cu.evalInSandbox(sourceText, sandbox, "1.8", ACTOR_URL, 1);
     return sandbox.exports;
   });
 
-// Ensure fetching a live TabActor form for the targeted app
-// (helps fetching the test actor registered dynamically)
-  var getUpdatedForm = function (client, tab) {
+  // Ensure fetching a live TabActor form for the targeted app
+  // (helps fetching the test actor registered dynamically)
+  let getUpdatedForm = function (client, tab) {
     return client.getTab({tab: tab})
-               .then(response => response.tab);
+                 .then(response => response.tab);
   };
 
-// Spawn an instance of the test actor for the given toolbox
+  // Spawn an instance of the test actor for the given toolbox
   exports.getTestActor = Task.async(function* (toolbox) {
     let client = toolbox.target.client;
     return getTestActor(client, toolbox.target.tab, toolbox);
   });
 
-
-// Sometimes, we need the test actor before opening or without a toolbox
-// then just create a front for the given `tab`
+  // Sometimes, we need the test actor before opening or without a toolbox then just
+  // create a front for the given `tab`
   exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
     let { DebuggerServer } = require("devtools/server/main");
     let { DebuggerClient } = require("devtools/shared/client/main");
 
-  // We need to spawn a client instance,
-  // but for that we have to first ensure a server is running
+    // We need to spawn a client instance,
+    // but for that we have to first ensure a server is running
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     let client = new DebuggerClient(DebuggerServer.connectPipe());
 
     yield client.connect();
 
-  // We also need to make sure the test actor is registered on the server.
-    yield registerTestActor(client);
+    // We also need to make sure the test actor is registered on the server.
+    yield exports.registerTestActor(client);
 
     return getTestActor(client, tab);
   });
 
-// Fetch the content of a URI
-  var request = function (uri) {
+  // Fetch the content of a URI
+  let request = function (uri) {
     return fetch(uri).then(({ content }) => content);
   };
 
-  var getTestActor = Task.async(function* (client, tab, toolbox) {
-  // We may have to update the form in order to get the dynamically registered
-  // test actor.
+  let getTestActor = Task.async(function* (client, tab, toolbox) {
+    // We may have to update the form in order to get the dynamically registered
+    // test actor.
     let form = yield getUpdatedForm(client, tab);
 
     let { TestActorFront } = yield loadFront();
 
     return new TestActorFront(client, form, toolbox);
   });
-
 })(this);
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -79,16 +79,27 @@ function* openTabAndSetupStorage(url) {
       };
       _getAllWindows(baseWindow);
 
       return windows;
     }
 
     let windows = getAllWindows(content);
     for (let win of windows) {
+      let readyState = win.document.readyState;
+      info(`Found a window: ${readyState}`);
+      if (readyState != "complete") {
+        yield new Promise(resolve => {
+          let onLoad = () => {
+            win.removeEventListener("load", onLoad);
+            resolve();
+          };
+          win.addEventListener("load", onLoad);
+        });
+      }
       if (win.setup) {
         yield win.setup();
       }
     }
   });
 
   // open storage inspector
   return yield openStoragePanel();
--- a/devtools/client/storage/test/storage-listings.html
+++ b/devtools/client/storage/test/storage-listings.html
@@ -21,17 +21,17 @@ document.cookie = "c1=foobar; expires=" 
 document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname;
 document.cookie = "c3=foobar-2; secure=true; expires=" +
   new Date(cookieExpiresTime2).toGMTString() + "; path=/";
 // ... and some local storage items ..
 localStorage.setItem("ls1", "foobar");
 localStorage.setItem("ls2", "foobar-2");
 // ... and finally some session storage items too
 sessionStorage.setItem("ss1", "foobar-3");
-dump("added cookies and stuff from main page\n");
+dump("added cookies and storage from main page\n");
 
 let idbGenerator = function*() {
   let request = indexedDB.open("idb1", 1);
   request.onerror = function() {
     throw new Error("error opening db connection");
   };
   let db = yield new Promise(done => {
     request.onupgradeneeded = event => {
@@ -82,17 +82,17 @@ let idbGenerator = function*() {
     };
   });
   // Prevents AbortError during close()
   yield new Promise(done => {
     request.onsuccess = done;
   });
   db2.close();
 
-  dump("added cookies and stuff from main page\n");
+  dump("added indexedDB from main page\n");
 };
 
 function deleteDB(dbName) {
   return new Promise(resolve => {
     dump("removing database " + dbName + " from " + document.location + "\n");
     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
   });
 }
--- a/devtools/client/storage/test/storage-secured-iframe.html
+++ b/devtools/client/storage/test/storage-secured-iframe.html
@@ -7,16 +7,17 @@ Iframe for testing multiple host detetio
   <meta charset="utf-8">
 </head>
 <body>
 <script type="application/javascript;version=1.7">
 "use strict";
 document.cookie = "sc1=foobar;";
 localStorage.setItem("iframe-s-ls1", "foobar");
 sessionStorage.setItem("iframe-s-ss1", "foobar-2");
+dump("added cookies and storage from secured iframe\n");
 
 let idbGenerator = function*() {
   let request = indexedDB.open("idb-s1", 1);
   request.onerror = function() {
     throw new Error("error opening db connection");
   };
   let db = yield new Promise(done => {
     request.onupgradeneeded = event => {
@@ -60,17 +61,17 @@ let idbGenerator = function*() {
   transaction = db2.transaction(["obj-s2"], "readwrite");
   let store3 = transaction.objectStore("obj-s2");
   store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
   yield new Promise(success => {
     transaction.oncomplete = success;
   });
 
   db2.close();
-  dump("added cookies and stuff from secured iframe\n");
+  dump("added indexedDB from secured iframe\n");
 };
 
 function deleteDB(dbName) {
   return new Promise(resolve => {
     dump("removing database " + dbName + " from " + document.location + "\n");
     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
   });
 }
--- a/devtools/client/storage/test/storage-unsecured-iframe.html
+++ b/devtools/client/storage/test/storage-unsecured-iframe.html
@@ -8,12 +8,12 @@ Iframe for testing multiple host detetio
 </head>
 <body>
 <script>
 "use strict";
 document.cookie = "uc1=foobar; domain=.example.org; path=/; secure=true";
 localStorage.setItem("iframe-u-ls1", "foobar");
 sessionStorage.setItem("iframe-u-ss1", "foobar1");
 sessionStorage.setItem("iframe-u-ss2", "foobar2");
-console.log("added cookies and stuff from unsecured iframe");
+dump("added cookies and storage from unsecured iframe\n");
 </script>
 </body>
 </html>
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -49,16 +49,17 @@ support-files =
   doc_uncached.css
   doc_uncached.html
   doc_xulpage.xul
   sync.html
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/shared/test/head.js
   !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor-registry.js
   !/devtools/client/shared/test/test-actor.js
 
 [browser_styleeditor_autocomplete.js]
 [browser_styleeditor_autocomplete-disabled.js]
 [browser_styleeditor_bug_740541_iframes.js]
 [browser_styleeditor_bug_851132_middle_click.js]
 [browser_styleeditor_bug_870339.js]
--- a/devtools/client/webaudioeditor/controller.js
+++ b/devtools/client/webaudioeditor/controller.js
@@ -64,16 +64,22 @@ var WebAudioEditorController = {
     // If not, get the JSON directly. Using the actor method is preferable so the client
     // knows exactly what methods are supported on the server.
     let actorHasDefinition = yield gTarget.actorHasMethod("webaudio", "getDefinition");
     if (actorHasDefinition) {
       AUDIO_NODE_DEFINITION = yield gFront.getDefinition();
     } else {
       AUDIO_NODE_DEFINITION = require("devtools/server/actors/utils/audionodes.json");
     }
+
+    // Make sure the backend is prepared to handle audio contexts.
+    // Since actors are created lazily on the first request to them, we need to send an
+    // early request to ensure the CallWatcherActor is running and watching for new window
+    // globals.
+    gFront.setup({ reload: false });
   }),
 
   /**
    * Remove events emitted by the current tab target.
    */
   destroy: function () {
     gTarget.off("will-navigate", this._onTabNavigated);
     gTarget.off("navigate", this._onTabNavigated);
@@ -128,21 +134,16 @@ var WebAudioEditorController = {
   },
 
   /**
    * Called for each location change in the debugged tab.
    */
   _onTabNavigated: Task.async(function* (event, {isFrameSwitching}) {
     switch (event) {
       case "will-navigate": {
-        // Make sure the backend is prepared to handle audio contexts.
-        if (!isFrameSwitching) {
-          yield gFront.setup({ reload: false });
-        }
-
         // Clear out current UI.
         this.reset();
 
         // When switching to an iframe, ensure displaying the reload button.
         // As the document has already been loaded without being hooked.
         if (isFrameSwitching) {
           $("#reload-notice").hidden = false;
           $("#waiting-notice").hidden = true;
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -6,23 +6,22 @@
  * Uses the editor front as the actors do not retain connect state.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [dest, osc, gain] = actors;
 
   info("Disconnecting oscillator...");
   osc.disconnect();
   yield Promise.all([
     waitForGraphRendered(panelWin, 3, 1),
     once(gAudioNodes, "disconnect")
   ]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
@@ -6,23 +6,22 @@
  * Uses the editor front as the actors do not retain connect state.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [dest, osc, gain] = actors;
 
   yield osc.disconnect();
 
   osc.connectParam(gain, "gain");
   yield Promise.all([
     waitForGraphRendered(panelWin, 3, 1, 1),
     once(gAudioNodes, "connect")
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
+++ b/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
@@ -10,17 +10,17 @@
 
 const BUG_1130901_URL = EXAMPLE_URL + "doc_bug_1130901.html";
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(BUG_1130901_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
+  let rendered = waitForGraphRendered(panelWin, 3, 0);
   reload(target);
-
-  yield waitForGraphRendered(panelWin, 3, 0);
+  yield rendered;
 
   ok(true, "Successfully created a node from AudioContext via `call`.");
   ok(true, "Successfully created a node from AudioContext via `apply`.");
 
   yield teardown(target);
 });
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
+++ b/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
@@ -11,19 +11,19 @@ const BUG_1112378_URL = EXAMPLE_URL + "d
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(BUG_1112378_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   loadFrameScripts();
 
+  let rendered = waitForGraphRendered(panelWin, 2, 0);
   reload(target);
-
-  yield waitForGraphRendered(panelWin, 2, 0);
+  yield rendered;
 
   let error = yield evalInDebuggee("throwError()");
   is(error.lineNumber, 21, "error has correct lineNumber");
   is(error.columnNumber, 11, "error has correct columnNumber");
   is(error.name, "TypeError", "error has correct name");
   is(error.message, "Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error has correct message");
   is(error.stringified, "TypeError: Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error is stringified correctly");
   is(error.instanceof, true, "error is correctly an instanceof TypeError");
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
@@ -8,23 +8,24 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
+
   let $tabbox = $("#web-audio-editor-tabs");
 
   // Oscillator node
   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
   yield waitForInspectorRender(panelWin, EVENTS);
   $tabbox.selectedIndex = 1;
 
   ok(isVisible($("#automation-graph-container")), "graph container should be visible");
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
@@ -8,25 +8,24 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, AutomationView } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
-
   // Oscillator node
   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
   yield waitForInspectorRender(panelWin, EVENTS);
   click(panelWin, $("#automation-tab"));
 
   ok(AutomationView._selectedParamName, "frequency",
     "AutomatioView is set on 'frequency'");
   ok($(".automation-param-button[data-param='frequency']").getAttribute("selected"),
--- a/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
@@ -9,20 +9,20 @@
 
 const BUG_1125817_URL = EXAMPLE_URL + "doc_bug_1125817.html";
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(BUG_1125817_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     once(gAudioNodes, "add", 2),
     once(gAudioNodes, "disconnect"),
     waitForGraphRendered(panelWin, 2, 0)
   ]);
+  reload(target);
+  yield events;
 
   ok(true, "Successfully disconnected a just-created node.");
 
   yield teardown(target);
 });
--- a/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
@@ -11,22 +11,22 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(DESTROY_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [created] = yield Promise.all([
+  let events = Promise.all([
     getNSpread(gAudioNodes, "add", 13),
     waitForGraphRendered(panelWin, 13, 2)
   ]);
+  reload(target);
+  let [created] = yield events;
 
   // Flatten arrays of event arguments and take the first (AudioNodeModel)
   // and get its ID.
   let actorIDs = created.map(ev => ev[0].id);
 
   // Click a soon-to-be dead buffer node
   yield clickGraphNode(panelWin, actorIDs[5]);
 
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
@@ -8,23 +8,22 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let panelWin = panel.panelWin;
   let { gFront, $, $$, InspectorView } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors, _] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 8),
     waitForGraphRendered(panel.panelWin, 8, 8)
   ]);
-
+  reload(target);
+  let [actors, _] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
 
   yield clickGraphNode(panelWin, nodeIds[1], true);
 
   ok(InspectorView.isVisible(), "InspectorView visible after selecting a node.");
   is(InspectorView.getCurrentAudioNode().id, nodeIds[1], "InspectorView has correct node set.");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
@@ -14,22 +14,22 @@ add_task(function* () {
 
   let currentTheme = Services.prefs.getCharPref("devtools.theme");
 
   ok(MARKER_STYLING.light, "Marker styling exists for light theme.");
   ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
 
   is(getFill($("#arrowhead")), MARKER_STYLING[currentTheme],
     "marker initially matches theme.");
 
   // Switch to light
   setTheme("light");
   is(getFill($("#arrowhead")), MARKER_STYLING.light,
     "marker styling matches light theme on change.");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
@@ -9,25 +9,24 @@ var connectCount = 0;
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
   gAudioNodes.on("connect", onConnectNode);
 
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [destId, oscId, gainId] = actors.map(actor => actor.actorID);
 
   ok(findGraphNode(panelWin, oscId).classList.contains("type-OscillatorNode"), "found OscillatorNode with class");
   ok(findGraphNode(panelWin, gainId).classList.contains("type-GainNode"), "found GainNode with class");
   ok(findGraphNode(panelWin, destId).classList.contains("type-AudioDestinationNode"), "found AudioDestinationNode with class");
   is(findGraphEdge(panelWin, oscId, gainId).toString(), "[object SVGGElement]", "found edge for osc -> gain");
   is(findGraphEdge(panelWin, gainId, destId).toString(), "[object SVGGElement]", "found edge for gain -> dest");
 
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
@@ -7,23 +7,22 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$ } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 8),
     waitForGraphRendered(panelWin, 8, 8)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let nodeIDs = actors.map(actor => actor.actorID);
 
   let types = ["AudioDestinationNode", "OscillatorNode", "GainNode", "ScriptProcessorNode",
                "OscillatorNode", "GainNode", "AudioBufferSourceNode", "BiquadFilterNode"];
 
 
   types.forEach((type, i) => {
     ok(findGraphNode(panelWin, nodeIDs[i]).classList.contains("type-" + type), "found " + type + " with class");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
@@ -5,23 +5,22 @@
  * Tests to ensure that selected nodes stay selected on graph redraw.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [dest, osc, gain] = actors;
 
   yield clickGraphNode(panelWin, gain.actorID);
   ok(findGraphNode(panelWin, gain.actorID).classList.contains("selected"),
     "Node selected once.");
 
   // Disconnect a node to trigger a rerender
   osc.disconnect();
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
@@ -7,23 +7,22 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 5),
     waitForGraphRendered(panelWin, 5, 2, 3)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let nodeIDs = actors.map(actor => actor.actorID);
 
   let [, carrier, gain, mod1, mod2] = nodeIDs;
 
   let edges = [
     [mod1, gain, "gain", "mod1 -> gain[gain]"],
     [mod2, carrier, "frequency", "mod2 -> carrier[frequency]"],
     [mod2, carrier, "detune", "mod2 -> carrier[detune]"]
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
@@ -5,23 +5,22 @@
  * Tests to ensure that param connections trigger graph redraws
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2, 0)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [dest, osc, gain] = actors;
 
   yield osc.disconnect();
 
   osc.connectParam(gain, "gain");
   yield waitForGraphRendered(panelWin, 3, 1, 1);
   ok(true, "Graph re-rendered upon param connection");
 
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
@@ -7,19 +7,19 @@
 
 const BUG_1141261_URL = EXAMPLE_URL + "doc_bug_1141261.html";
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(BUG_1141261_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 1, 0)
   ]);
+  reload(target);
+  yield events;
 
   ok(true, "Graph correctly shows gain node as disconnected");
 
   yield teardown(target);
 });
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
@@ -7,23 +7,22 @@
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
-
+  reload(target);
+  let [actors] = yield events;
   let [destId, oscId, gainId] = actors.map(actor => actor.actorID);
 
   ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
     "No nodes selected on start. (destination)");
   ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
     "No nodes selected on start. (oscillator)");
   ok(!findGraphNode(panelWin, gainId).classList.contains("selected"),
     "No nodes selected on start. (gain)");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
@@ -8,36 +8,36 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, ContextView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   yield Promise.all([
+    waitForGraphRendered(panelWin, 3, 2),
     reload(target),
-    waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   is(ContextView.getCurrentScale(), 1, "Default graph scale is 1.");
   is(ContextView.getCurrentTranslation()[0], 20, "Default x-translation is 20.");
   is(ContextView.getCurrentTranslation()[1], 20, "Default y-translation is 20.");
 
   // Change both attribute and D3's internal store
   panelWin.d3.select("#graph-target").attr("transform", "translate([100, 400]) scale(10)");
   ContextView._zoomBinding.scale(10);
   ContextView._zoomBinding.translate([100, 400]);
 
   is(ContextView.getCurrentScale(), 10, "After zoom, scale is 10.");
   is(ContextView.getCurrentTranslation()[0], 100, "After zoom, x-translation is 100.");
   is(ContextView.getCurrentTranslation()[1], 400, "After zoom, y-translation is 400.");
 
   yield Promise.all([
+    waitForGraphRendered(panelWin, 3, 2),
     reload(target),
-    waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   is(ContextView.getCurrentScale(), 1, "After refresh, graph scale is 1.");
   is(ContextView.getCurrentTranslation()[0], 20, "After refresh, x-translation is 20.");
   is(ContextView.getCurrentTranslation()[1], 20, "After refresh, y-translation is 20.");
 
   yield teardown(target);
 });
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
@@ -5,22 +5,22 @@
  * Tests that nodes are correctly bypassed when bypassing.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   // Wait for the node to be set as well as the inspector to come fully into the view
   yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]), true);
 
   let $bypass = $("toolbarbutton.bypass");
 
   is((yield actors[1].isBypassed()), false, "AudioNodeActor is not bypassed by default.");
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -9,22 +9,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
 
   // Open inspector pane
   $("#inspector-pane-toggle").click();
   yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
 
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
@@ -9,41 +9,42 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
 
   // Open inspector pane
   $("#inspector-pane-toggle").click();
   yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
 
   let newInspectorWidth = 500;
 
   // Setting width to new_inspector_width
   $("#web-audio-inspector").setAttribute("width", newInspectorWidth);
-  reload(target);
 
   // Width should be 500 after reloading
-  [actors] = yield Promise.all([
+  events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  [actors] = yield events;
   nodeIds = actors.map(actor => actor.actorID);
 
   // Open inspector pane
   $("#inspector-pane-toggle").click();
   yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
 
   yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]));
 
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector.js
@@ -9,22 +9,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
   ok(isVisible($("#web-audio-editor-details-pane-empty")),
     "InspectorView empty message should show when no node's selected.");
   ok(!isVisible($("#web-audio-editor-tabs")),
     "InspectorView tabs view should be hidden when no node's selected.");
 
--- a/devtools/client/webaudioeditor/test/browser_wa_navigate.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_navigate.js
@@ -6,33 +6,33 @@
  * the audio graph if both pages have an AudioContext.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
-  reload(target);
-
-  var [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  yield events;
 
   var { nodes, edges } = countGraphObjects(panelWin);
   is(nodes, 3, "should only be 3 nodes.");
   is(edges, 2, "should only be 2 edges.");
 
-  navigate(target, SIMPLE_NODES_URL);
-
-  var [actors] = yield Promise.all([
+  events = Promise.all([
     getN(gFront, "create-node", 15),
     waitForGraphRendered(panelWin, 15, 0)
   ]);
+  navigate(target, SIMPLE_NODES_URL);
+  yield events;
 
   is($("#reload-notice").hidden, true,
     "The 'reload this page' notice should be hidden after context found after navigation.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should be hidden after context found after navigation.");
   is($("#content").hidden, false,
     "The tool's content should reappear without closing and reopening the toolbox.");
 
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
@@ -8,22 +8,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
   // Wait for the node to be set as well as the inspector to come fully into the view
   yield Promise.all([
     waitForInspectorRender(panelWin, EVENTS),
     once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
@@ -8,22 +8,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 8),
     waitForGraphRendered(panelWin, 8, 8)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   click(panelWin, findGraphNode(panelWin, nodeIds[3]));
   // Wait for the node to be set as well as the inspector to come fully into the view
   yield Promise.all([
     waitForInspectorRender(panelWin, EVENTS),
     once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED),
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -39,26 +39,26 @@ add_task(function* () {
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   // Auto enable getUserMedia
   let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
   Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
 
-  reload(target);
-
   yield loadFrameScripts();
 
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 4),
     waitForGraphRendered(panelWin, 4, 0)
   ]);
+  reload(target);
+  let [actors] = yield events;
+  let nodeIds = actors.map(actor => actor.actorID);
 
-  let nodeIds = actors.map(actor => actor.actorID);
   let types = [
     "AudioDestinationNode", "MediaElementAudioSourceNode",
     "MediaStreamAudioSourceNode", "MediaStreamAudioDestinationNode"
   ];
 
   let defaults = yield Promise.all(types.map(type => nodeDefaultValues(type)));
 
   for (let i = 0; i < types.length; i++) {
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
@@ -9,22 +9,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
   yield waitForInspectorRender(panelWin, EVENTS);
   checkVariableView(gVars, 0, {
     "curve": "Float32Array"
   }, "WaveShaper's `curve` is listed as an `Float32Array`.");
 
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
@@ -9,25 +9,26 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
   yield loadFrameScripts();
 
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     getN(gFront, "create-node", 15),
     waitForGraphRendered(panelWin, 15, 0)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
+
   let types = [
     "AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
     "AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
     "PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
     "DynamicsCompressorNode", "OscillatorNode"
   ];
 
   let defaults = yield Promise.all(types.map(type => nodeDefaultValues(type)));
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
@@ -8,22 +8,22 @@
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   // Gain node
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
   yield waitForInspectorRender(panelWin, EVENTS);
 
   ok(isVisible($("#properties-content")), "Parameters shown when they exist.");
   ok(!isVisible($("#properties-empty")),
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
@@ -6,32 +6,32 @@
  * the graph.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  yield events;
 
   let { nodes, edges } = countGraphObjects(panelWin);
   is(nodes, 3, "should only be 3 nodes.");
   is(edges, 2, "should only be 2 edges.");
 
-  reload(target);
-
-  [actors] = yield Promise.all([
+  events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  yield events;
 
   ({ nodes, edges } = countGraphObjects(panelWin));
   is(nodes, 3, "after reload, should only be 3 nodes.");
   is(edges, 2, "after reload, should only be 2 edges.");
 
   yield teardown(target);
 });
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
@@ -6,38 +6,38 @@
  * the inspector and selected node.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, InspectorView } = panelWin;
 
-  reload(target);
-
-  let [actors] = yield Promise.all([
+  let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  let [actors] = yield events;
   let nodeIds = actors.map(actor => actor.actorID);
 
   yield clickGraphNode(panelWin, nodeIds[1], true);
   ok(InspectorView.isVisible(), "InspectorView visible after selecting a node.");
   is(InspectorView.getCurrentAudioNode().id, nodeIds[1], "InspectorView has correct node set.");
 
   /**
    * Reload
    */
 
-  reload(target);
-
-  [actors] = yield Promise.all([
+  events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
+  reload(target);
+  [actors] = yield events;
   nodeIds = actors.map(actor => actor.actorID);
 
   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
   is(InspectorView.getCurrentAudioNode(), null,
     "InspectorView has no current node set on reset.");
 
   yield clickGraphNode(panelWin, nodeIds[2], true);
   ok(InspectorView.isVisible(),
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -203,21 +203,23 @@ function teardown(aTarget) {
 // us to listen for `n` events and return a promise resolving to them.
 //
 // Takes a `front` object that is an event emitter, the number of
 // programs that should be listened to and waited on, and an optional
 // `onAdd` function that calls with the entire actors array on program link
 function getN(front, eventName, count, spread) {
   let actors = [];
   let deferred = Promise.defer();
+  info(`Waiting for ${count} ${eventName} events`);
   front.on(eventName, function onEvent(...args) {
     let actor = args[0];
     if (actors.length !== count) {
       actors.push(spread ? args : actor);
     }
+    info(`Got ${actors.length} / ${count} ${eventName} events`);
     if (actors.length === count) {
       front.off(eventName, onEvent);
       deferred.resolve(actors);
     }
   });
   return deferred.promise;
 }
 
@@ -232,18 +234,21 @@ function getNSpread(front, eventName, co
 /**
  * Waits for the UI_GRAPH_RENDERED event to fire, but only
  * resolves when the graph was rendered with the correct count of
  * nodes and edges.
  */
 function waitForGraphRendered(front, nodeCount, edgeCount, paramEdgeCount) {
   let deferred = Promise.defer();
   let eventName = front.EVENTS.UI_GRAPH_RENDERED;
+  info(`Wait for graph rendered with ${nodeCount} nodes, ${edgeCount} edges`);
   front.on(eventName, function onGraphRendered(_, nodes, edges, pEdges) {
     let paramEdgesDone = paramEdgeCount != null ? paramEdgeCount === pEdges : true;
+    info(`Got graph rendered with ${nodes} / ${nodeCount} nodes, ` +
+         `${edges} / ${edgeCount} edges`);
     if (nodes === nodeCount && edges === edgeCount && paramEdgesDone) {
       front.off(eventName, onGraphRendered);
       deferred.resolve();
     }
   });
   return deferred.promise;
 }
 
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -262,17 +262,17 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_bug_632817.js]
 [browser_webconsole_bug_642108_pruneTest.js]
 [browser_webconsole_autocomplete_and_selfxss.js]
 subsuite = clipboard
 [browser_webconsole_bug_644419_log_limits.js]
 [browser_webconsole_bug_646025_console_file_location.js]
 [browser_webconsole_bug_651501_document_body_autocomplete.js]
 [browser_webconsole_bug_653531_highlighter_console_helper.js]
-skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+skip-if = true # Requires direct access to content nodes
 [browser_webconsole_bug_658368_time_methods.js]
 [browser_webconsole_bug_659907_console_dir.js]
 [browser_webconsole_bug_660806_history_nav.js]
 [browser_webconsole_bug_664131_console_group.js]
 [browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js]
 [browser_webconsole_bug_704295.js]
 [browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js]
 [browser_webconsole_bug_737873_mixedcontent.js]
--- a/devtools/client/webide/test/browser_tabs.js
+++ b/devtools/client/webide/test/browser_tabs.js
@@ -4,18 +4,16 @@
 
 const TEST_URI = "http://example.com/browser/devtools/client/webide/test/doc_tabs.html";
 
 function test() {
   waitForExplicitFinish();
   requestCompleteLog();
 
   Task.spawn(function* () {
-    const { DebuggerServer } = require("devtools/server/main");
-
     // Since we test the connections set below, destroy the server in case it
     // was left open.
     DebuggerServer.destroy();
     DebuggerServer.init();
     DebuggerServer.addBrowserActors();
 
     let tab = yield addTab(TEST_URI);
 
--- a/devtools/client/webide/test/head.js
+++ b/devtools/client/webide/test/head.js
@@ -1,23 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
-const {gDevTools} = require("devtools/client/framework/devtools");
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
+const { gDevTools } = require("devtools/client/framework/devtools");
 const promise = require("promise");
 const Services = require("Services");
-const {Task} = require("devtools/shared/task");
-const {AppProjects} = require("devtools/client/webide/modules/app-projects");
+const { Task } = require("devtools/shared/task");
+const { AppProjects } = require("devtools/client/webide/modules/app-projects");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const { DebuggerServer } = require("devtools/server/main");
 DevToolsUtils.testing = true;
 
 var TEST_BASE;
 if (window.location === "chrome://browser/content/browser.xul") {
   TEST_BASE = "chrome://mochitests/content/browser/devtools/client/webide/test/";
 } else {
   TEST_BASE = "chrome://mochitests/content/chrome/devtools/client/webide/test/";
 }
@@ -40,38 +41,37 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
   Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
   Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
   Services.prefs.clearUserPref("devtools.webide.busyTimeout");
   Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
   Services.prefs.clearUserPref("devtools.webide.lastConnectedRuntime");
 });
 
-function openWebIDE(autoInstallAddons) {
+var openWebIDE = Task.async(function* (autoInstallAddons) {
   info("opening WebIDE");
 
   Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons);
   Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", !!autoInstallAddons);
 
-  let deferred = promise.defer();
-
   let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
   let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
 
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad);
-    info("WebIDE open");
-    SimpleTest.requestCompleteLog();
-    SimpleTest.executeSoon(() => {
-      deferred.resolve(win);
+  yield new Promise(resolve => {
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad);
+      SimpleTest.requestCompleteLog();
+      SimpleTest.executeSoon(resolve);
     });
   });
 
-  return deferred.promise;
-}
+  info("WebIDE open");
+
+  return win;
+});
 
 function closeWebIDE(win) {
   info("Closing WebIDE");
 
   let deferred = promise.defer();
 
   Services.prefs.clearUserPref("devtools.webide.widget.enabled");
 
@@ -225,8 +225,24 @@ function connectToLocalRuntime(win) {
   items[1].click();
   return updated;
 }
 
 function handleError(aError) {
   ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
   finish();
 }
+
+function waitForConnectionChange(expectedState, count = 1) {
+  return new Promise(resolve => {
+    let onConnectionChange = (_, state) => {
+      if (state != expectedState) {
+        return;
+      }
+      if (--count != 0) {
+        return;
+      }
+      DebuggerServer.off("connectionchange", onConnectionChange);
+      resolve();
+    };
+    DebuggerServer.on("connectionchange", onConnectionChange);
+  });
+}
--- a/devtools/client/webide/test/test_autoconnect_runtime.html
+++ b/devtools/client/webide/test/test_autoconnect_runtime.html
@@ -14,18 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function*() {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
 
           let win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
 
@@ -48,40 +46,44 @@
           };
           win.AppManager.runtimeList.usb.push(fakeRuntime);
           win.AppManager.update("runtime-list");
 
           let panelNode = docRuntime.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
           is(items.length, 1, "Found one runtime button");
 
-          let deferred = promise.defer();
-          win.AppManager.connection.once(
-              win.Connection.Events.CONNECTED,
-              () => deferred.resolve());
+          let connectionsChanged = waitForConnectionChange("opened", 2);
           items[0].click();
 
           ok(win.document.querySelector("window").className, "busy", "UI is busy");
           yield win.UI._busyPromise;
-          is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
+
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Connected");
+
+          connectionsChanged = waitForConnectionChange("closed", 2);
 
           yield nextTick();
-
           yield closeWebIDE(win);
 
+          yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
 
+          connectionsChanged = waitForConnectionChange("opened", 2);
+
           win = yield openWebIDE();
 
           win.AppManager.runtimeList.usb.push(fakeRuntime);
           win.AppManager.update("runtime-list");
 
           yield waitForUpdate(win, "runtime-targets");
 
-          is(Object.keys(DebuggerServer._connections).length, 1, "Automatically reconnected");
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Automatically reconnected");
 
           yield win.Cmds.disconnectRuntime();
 
           yield closeWebIDE(win);
 
           DebuggerServer.destroy();
 
           SimpleTest.finish();
--- a/devtools/client/webide/test/test_autoselect_project.html
+++ b/devtools/client/webide/test/test_autoselect_project.html
@@ -14,72 +14,78 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
 
           let win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
 
           let panelNode = docRuntime.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-other");
           is(items.length, 2, "Found 2 runtime buttons");
 
           // Connect to local runtime
+          let connectionsChanged = waitForConnectionChange("opened", 2);
           items[1].click();
 
           yield waitForUpdate(win, "runtime-targets");
 
-          is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Locally connected");
 
           ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
 
           // Select main process
           yield win.Cmds.showProjectPanel();
           yield waitForUpdate(win, "runtime-targets");
           SimpleTest.executeSoon(() => {
             docProject.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
           });
 
           yield waitForUpdate(win, "project");
 
           let lastProject = Services.prefs.getCharPref("devtools.webide.lastSelectedProject");
           is(lastProject, "mainProcess:", "Last project is main process");
 
+          connectionsChanged = waitForConnectionChange("closed", 2);
+
           yield nextTick();
           yield closeWebIDE(win);
 
+          yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
 
+          connectionsChanged = waitForConnectionChange("opened", 2);
+
           // Re-open, should reselect main process after connection
           win = yield openWebIDE();
 
           docRuntime = getRuntimeDocument(win);
 
           panelNode = docRuntime.querySelector("#runtime-panel");
           items = panelNode.querySelectorAll(".runtime-panel-item-other");
           is(items.length, 2, "Found 2 runtime buttons");
 
           // Connect to local runtime
           items[1].click();
 
           yield waitForUpdate(win, "runtime-targets");
 
-          is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Locally connected");
           ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
           is(win.AppManager.selectedProject.type, "mainProcess", "Main process reselected");
 
           // Wait for the toolbox to be fully loaded
           yield win.UI.toolboxPromise;
 
           // If we happen to pass a project object targeting the same context,
           // here, the main process, the `selectedProject` attribute shouldn't be updated
--- a/devtools/client/webide/test/test_device_permissions.html
+++ b/devtools/client/webide/test/test_device_permissions.html
@@ -14,18 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
 
           let win = yield openWebIDE();
 
           let permIframe = win.document.querySelector("#deck-panel-permissionstable");
--- a/devtools/client/webide/test/test_device_preferences.html
+++ b/devtools/client/webide/test/test_device_preferences.html
@@ -14,18 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
 
           let win = yield openWebIDE();
 
           let prefIframe = win.document.querySelector("#deck-panel-devicepreferences");
--- a/devtools/client/webide/test/test_device_runtime.html
+++ b/devtools/client/webide/test/test_device_runtime.html
@@ -14,18 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
 
           let win = yield openWebIDE();
 
           let detailsIframe = win.document.querySelector("#deck-panel-runtimedetails");
--- a/devtools/client/webide/test/test_device_settings.html
+++ b/devtools/client/webide/test/test_device_settings.html
@@ -14,18 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function*() {
-          const { DebuggerServer } = require("devtools/server/main");
-
           if (SpecialPowers.isMainProcess()) {
             Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
           }
 
           if (!DebuggerServer.initialized) {
             DebuggerServer.init();
             DebuggerServer.addBrowserActors();
           }
--- a/devtools/client/webide/test/test_fullscreenToolbox.html
+++ b/devtools/client/webide/test/test_fullscreenToolbox.html
@@ -23,17 +23,16 @@
         docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
         return deferred.promise;
       }
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          const { DebuggerServer } = require("devtools/server/main");
           let win = yield openWebIDE();
           let docProject = getProjectDocument(win);
           let docRuntime = getRuntimeDocument(win);
           win.AppManager.update("runtime-list");
 
           yield connectToLocal(win, docRuntime);
 
           // Select main process
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -13,17 +13,16 @@
   </head>
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        const { DebuggerServer } = require("devtools/server/main");
         let win;
 
         SimpleTest.registerCleanupFunction(() => {
           Task.spawn(function*() {
             if (win) {
               yield closeWebIDE(win);
             }
             DebuggerServer.destroy();
@@ -93,27 +92,24 @@
 
           yield winProject.projectList.importPackagedApp(packagedAppLocation);
           yield waitForUpdate(win, "project-validated");
 
           let panelNode = docRuntime.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
           is(items.length, 3, "Found 3 runtime buttons");
 
-          let deferred = promise.defer();
-          win.AppManager.connection.once(
-              win.Connection.Events.CONNECTED,
-              () => deferred.resolve());
-
+          let connectionsChanged = waitForConnectionChange("opened", 2);
           items[0].click();
 
           ok(win.document.querySelector("window").className, "busy", "UI is busy");
           yield win.UI._busyPromise;
 
-          is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Connected");
 
           yield waitForUpdate(win, "runtime-global-actors");
 
           ok(isPlayActive(), "play button is enabled 1");
           ok(!isStopActive(), "stop button is disabled 1");
           let oldProject = win.AppManager.selectedProject;
           win.AppManager.selectedProject = null;
 
@@ -124,29 +120,33 @@
           win.AppManager._selectedProject = oldProject;
           win.UI.updateCommands();
 
           yield nextTick();
 
           ok(isPlayActive(), "play button is enabled 3");
           ok(!isStopActive(), "stop button is disabled 3");
 
+          connectionsChanged = waitForConnectionChange("closed", 2);
           yield win.Cmds.disconnectRuntime();
 
+          yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
 
           ok(win.AppManager.selectedProject, "A project is still selected");
           ok(!isPlayActive(), "play button is disabled 4");
           ok(!isStopActive(), "stop button is disabled 4");
 
+          connectionsChanged = waitForConnectionChange("opened", 2);
           docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
 
           yield waitForUpdate(win, "runtime-targets");
 
-          is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
+          yield connectionsChanged;
+          is(Object.keys(DebuggerServer._connections).length, 2, "Locally connected");
 
           ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
 
           // Select main process
           SimpleTest.executeSoon(() => {
             docProject.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
           });
 
--- a/devtools/client/webide/test/test_telemetry.html
+++ b/devtools/client/webide/test/test_telemetry.html
@@ -14,17 +14,16 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       const Telemetry = require("devtools/client/shared/telemetry");
       const { _DeprecatedUSBRuntime, _WiFiRuntime, _SimulatorRuntime,
               _gRemoteRuntime, _gLocalRuntime, RuntimeTypes }
             = require("devtools/client/webide/modules/runtimes");
-      const { DebuggerServer } = require("devtools/server/main");
 
       // Because we need to gather stats for the period of time that a tool has
       // been opened we make use of setTimeout() to create tool active times.
       const TOOL_DELAY = 200;
 
       function patchTelemetry() {
         Telemetry.prototype.telemetryInfo = {};
         Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/devtools/client/webide/test/test_toolbox.html
+++ b/devtools/client/webide/test/test_toolbox.html
@@ -10,18 +10,16 @@
     <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
     <script type="application/javascript;version=1.8" src="head.js"></script>
     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   </head>
 
   <body>
 
     <script type="application/javascript;version=1.8">
-      const { DebuggerServer } = require("devtools/server/main");
-
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         let win;
 
         SimpleTest.registerCleanupFunction(() => {
           Task.spawn(function*() {
             if (win) {
--- a/devtools/server/actors/call-watcher.js
+++ b/devtools/server/actors/call-watcher.js
@@ -228,19 +228,23 @@ var FunctionCallActor = protocol.ActorCl
  */
 var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this._onGlobalCreated = this._onGlobalCreated.bind(this);
     this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
     this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
+    on(this.tabActor, "window-ready", this._onGlobalCreated);
+    on(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
   },
   destroy: function (conn) {
     protocol.Actor.prototype.destroy.call(this, conn);
+    off(this.tabActor, "window-ready", this._onGlobalCreated);
+    off(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
     this.finalize();
   },
 
   /**
    * Lightweight listener invoked whenever an instrumented function is called
    * while recording. We're doing this to avoid the event emitter overhead,
    * since this is expected to be a very hot function.
    */
@@ -259,19 +263,16 @@ var CallWatcherActor = exports.CallWatch
     this._timestampEpoch = 0;
 
     this._functionCalls = [];
     this._tracedGlobals = tracedGlobals || [];
     this._tracedFunctions = tracedFunctions || [];
     this._holdWeak = !!holdWeak;
     this._storeCalls = !!storeCalls;
 
-    on(this.tabActor, "window-ready", this._onGlobalCreated);
-    on(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
-
     if (startRecording) {
       this.resumeRecording();
     }
     if (performReload) {
       this.tabActor.window.location.reload();
     }
   },
 
@@ -282,19 +283,16 @@ var CallWatcherActor = exports.CallWatch
    */
   finalize: function () {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
     this._finalized = true;
 
-    off(this.tabActor, "window-ready", this._onGlobalCreated);
-    off(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
-
     this._tracedGlobals = null;
     this._tracedFunctions = null;
   },
 
   /**
    * Returns whether the instrumented function calls are currently recorded.
    */
   isRecording: function () {
@@ -330,16 +328,20 @@ var CallWatcherActor = exports.CallWatch
   eraseRecording: function () {
     this._functionCalls = [];
   },
 
   /**
    * Invoked whenever the current tab actor's document global is created.
    */
   _onGlobalCreated: function ({window, id, isTopLevel}) {
+    if (!this._initialized) {
+      return;
+    }
+
     // TODO: bug 981748, support more than just the top-level documents.
     if (!isTopLevel) {
       return;
     }
 
     let self = this;
     this._tracedWindowId = id;
 
--- a/devtools/server/actors/childtab.js
+++ b/devtools/server/actors/childtab.js
@@ -1,14 +1,15 @@
 /* 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/. */
 
 "use strict";
 
+var { Cr } = require("chrome");
 var { TabActor } = require("devtools/server/actors/webbrowser");
 
 /**
  * Tab actor for documents living in a child process.
  *
  * Depends on TabActor, defined in webbrowser.js.
  */
 
@@ -49,20 +50,32 @@ Object.defineProperty(ContentActor.proto
     return this.window.document.title;
   },
   enumerable: true,
   configurable: true
 });
 
 ContentActor.prototype.exit = function () {
   if (this._sendForm) {
-    this._chromeGlobal.removeMessageListener("debug:form", this._sendForm);
+    try {
+      this._chromeGlobal.removeMessageListener("debug:form", this._sendForm);
+    } catch (e) {
+      if (e.result != Cr.NS_ERROR_NULL_POINTER) {
+        throw e;
+      }
+      // In some cases, especially when using messageManagers in non-e10s mode, we reach
+      // this point with a dead messageManager which only throws errors but does not
+      // seem to indicate in any other way that it is dead.
+    }
     this._sendForm = null;
   }
-  return TabActor.prototype.exit.call(this);
+
+  TabActor.prototype.exit.call(this);
+
+  this._chromeGlobal = null;
 };
 
 /**
  * On navigation events, our URL and/or title may change, so we update our
  * counterpart in the parent process that participates in the tab list.
  */
 ContentActor.prototype._sendForm = function () {
   this._chromeGlobal.sendAsyncMessage("debug:form", this.form());
--- a/devtools/server/actors/director-manager.js
+++ b/devtools/server/actors/director-manager.js
@@ -1,10 +1,8 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const events = require("sdk/event/core");
 const protocol = require("devtools/shared/protocol");
@@ -120,18 +118,23 @@ var MessagePortActor = exports.MessagePo
    * raise an exception if the port is null
    */
   close: function () {
     if (!this.port) {
       console.error(ERR_MESSAGEPORT_FINALIZED);
       return;
     }
 
-    this.port.onmessage = null;
-    this.port.close();
+    try {
+      this.port.onmessage = null;
+      this.port.close();
+    } catch (e) {
+      // The port might be a dead object
+      console.error(e);
+    }
   },
 
   finalize: function () {
     this.close();
     this.port = null;
   },
 });
 
--- a/devtools/server/actors/director-registry.js
+++ b/devtools/server/actors/director-registry.js
@@ -146,79 +146,80 @@ exports.setupParentProcess = function se
         return DirectorRegistry.list();
       default:
         console.error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD, msg.json.method);
         throw new Error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD);
     }
   }
 };
 
-// skip child setup if this actor module is not running in a child process
-if (DebuggerServer.isInChildProcess) {
-  setupChildProcess();
-}
-
-function setupChildProcess() {
-  const { sendSyncMessage } = DebuggerServer.parentMessageManager;
-
-  DebuggerServer.setupInParent({
-    module: "devtools/server/actors/director-registry",
-    setupParent: "setupParentProcess"
-  });
-
-  DirectorRegistry.install = notImplemented.bind(null, "install");
-  DirectorRegistry.uninstall = notImplemented.bind(null, "uninstall");
-  DirectorRegistry.clear = notImplemented.bind(null, "clear");
-
-  DirectorRegistry.get = callParentProcess.bind(null, "get");
-  DirectorRegistry.list = callParentProcess.bind(null, "list");
-
-  /* child process helpers */
-
-  function notImplemented(method) {
-    console.error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD, method);
-    throw Error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD);
-  }
-
-  function callParentProcess(method, ...args) {
-    var reply = sendSyncMessage("debug:director-registry-request", {
-      method: method,
-      args: args
-    });
-
-    if (reply.length === 0) {
-      console.error(ERR_DIRECTOR_CHILD_NO_REPLY);
-      throw Error(ERR_DIRECTOR_CHILD_NO_REPLY);
-    } else if (reply.length > 1) {
-      console.error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
-      throw Error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
-    }
-
-    return reply[0];
-  }
-}
-
 /**
  * The DirectorRegistry Actor is a global actor which manages install/uninstall of
  * director scripts definitions.
  */
 const DirectorRegistryActor = exports.DirectorRegistryActor = protocol.ActorClassWithSpec(directorRegistrySpec, {
   /* init & destroy methods */
   initialize: function (conn, parentActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
+    this.maybeSetupChildProcess(conn);
   },
   destroy: function (conn) {
     protocol.Actor.prototype.destroy.call(this, conn);
     this.finalize();
   },
 
   finalize: function () {
     // nothing to cleanup
   },
 
+  maybeSetupChildProcess(conn) {
+    // skip child setup if this actor module is not running in a child process
+    if (!DebuggerServer.isInChildProcess) {
+      return;
+    }
+
+    const { sendSyncMessage } = conn.parentMessageManager;
+
+    conn.setupInParent({
+      module: "devtools/server/actors/director-registry",
+      setupParent: "setupParentProcess"
+    });
+
+    DirectorRegistry.install = notImplemented.bind(null, "install");
+    DirectorRegistry.uninstall = notImplemented.bind(null, "uninstall");
+    DirectorRegistry.clear = notImplemented.bind(null, "clear");
+
+    DirectorRegistry.get = callParentProcess.bind(null, "get");
+    DirectorRegistry.list = callParentProcess.bind(null, "list");
+
+    /* child process helpers */
+
+    function notImplemented(method) {
+      console.error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD, method);
+      throw Error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD);
+    }
+
+    function callParentProcess(method, ...args) {
+      var reply = sendSyncMessage("debug:director-registry-request", {
+        method: method,
+        args: args
+      });
+
+      if (reply.length === 0) {
+        console.error(ERR_DIRECTOR_CHILD_NO_REPLY);
+        throw Error(ERR_DIRECTOR_CHILD_NO_REPLY);
+      } else if (reply.length > 1) {
+        console.error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
+        throw Error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
+      }
+
+      return reply[0];
+    }
+  },
+
   /**
    * Install a new director-script definition.
    *
    * @param String id
    *        The director-script definition identifier.
    * @param String scriptCode
    *        The director-script javascript source.
    * @param Object scriptOptions
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -191,16 +191,24 @@ RootActor.prototype = {
       from: this.actorID,
       applicationType: this.applicationType,
       /* This is not in the spec, but it's used by tests. */
       testConnectionPrefix: this.conn.prefix,
       traits: this.traits
     };
   },
 
+  forwardingCancelled: function (prefix) {
+    return {
+      from: this.actorID,
+      type: "forwardingCancelled",
+      prefix,
+    };
+  },
+
   /**
    * Disconnects the actor from the browser window.
    */
   disconnect: function () {
     /* Tell the live lists we aren't watching any more. */
     if (this._parameters.tabList) {
       this._parameters.tabList.onListChanged = null;
     }
--- a/devtools/server/actors/utils/actor-registry-utils.js
+++ b/devtools/server/actors/utils/actor-registry-utils.js
@@ -15,19 +15,31 @@ const promise = require("promise");
  * Support for actor registration. Main used by ActorRegistryActor
  * for dynamic registration of new actors.
  *
  * @param sourceText {String} Source of the actor implementation
  * @param fileName {String} URL of the actor module (for proper stack traces)
  * @param options {Object} Configuration object
  */
 exports.registerActor = function (sourceText, fileName, options) {
+  // Register in the current process
+  exports.registerActorInCurrentProcess(sourceText, fileName, options);
+  // Register in any child processes
+  return DebuggerServer.setupInChild({
+    module: "devtools/server/actors/utils/actor-registry-utils",
+    setupChild: "registerActorInCurrentProcess",
+    args: [sourceText, fileName, options],
+    waitForEval: true
+  });
+};
+
+exports.registerActorInCurrentProcess = function (sourceText, fileName, options) {
   const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();
   const sandbox = Cu.Sandbox(principal);
-  const exports = sandbox.exports = {};
+  sandbox.exports = {};
   sandbox.require = require;
 
   Cu.evalInSandbox(sourceText, sandbox, "1.8", fileName, 1);
 
   let { prefix, constructor, type } = options;
 
   if (type.global && !DebuggerServer.globalActorFactories.hasOwnProperty(prefix)) {
     DebuggerServer.addGlobalActor({
@@ -37,41 +49,30 @@ exports.registerActor = function (source
   }
 
   if (type.tab && !DebuggerServer.tabActorFactories.hasOwnProperty(prefix)) {
     DebuggerServer.addTabActor({
       constructorName: constructor,
       constructorFun: sandbox[constructor]
     }, prefix);
   }
-
-  // Also register in all child processes in case the current scope
-  // is chrome parent process.
-  if (!DebuggerServer.isInChildProcess) {
-    return DebuggerServer.setupInChild({
-      module: "devtools/server/actors/utils/actor-registry-utils",
-      setupChild: "registerActor",
-      args: [sourceText, fileName, options],
-      waitForEval: true
-    });
-  }
-  return promise.resolve();
 };
 
 exports.unregisterActor = function (options) {
+  // Unregister in the current process
+  exports.unregisterActorInCurrentProcess(options);
+  // Unregister in any child processes
+  DebuggerServer.setupInChild({
+    module: "devtools/server/actors/utils/actor-registry-utils",
+    setupChild: "unregisterActorInCurrentProcess",
+    args: [options]
+  });
+};
+
+exports.unregisterActorInCurrentProcess = function (options) {
   if (options.tab) {
     DebuggerServer.removeTabActor(options);
   }
 
   if (options.global) {
     DebuggerServer.removeGlobalActor(options);
   }
-
-  // Also unregister it from all child processes in case the current
-  // scope is chrome parent process.
-  if (!DebuggerServer.isInChildProcess) {
-    DebuggerServer.setupInChild({
-      module: "devtools/server/actors/utils/actor-registry-utils",
-      setupChild: "unregisterActor",
-      args: [options]
-    });
-  }
 };
--- a/devtools/server/actors/webaudio.js
+++ b/devtools/server/actors/webaudio.js
@@ -699,16 +699,24 @@ var WebAudioActor = exports.WebAudioActo
     }
   },
 
   /**
    * Ensures that the new global has recording on
    * so we can proxy the function calls.
    */
   _onGlobalCreated: function () {
+    // Used to track when something is happening with the web audio API
+    // the first time, to ultimately fire `start-context` event
+    this._firstNodeCreated = false;
+
+    // Clear out stored nativeIDs on reload as we do not want to track
+    // AudioNodes that are no longer on this document.
+    this._nativeToActorID.clear();
+
     this._callWatcher.resumeRecording();
   },
 
   /**
    * Fired when an automation event is added to an AudioNode.
    */
   _onAutomationEvent: function ({node, paramName, eventName, args}) {
     emit(this, "automation-event", {
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -310,20 +310,16 @@ BrowserTabList.prototype._getChildren = 
   return gBrowser.browsers.filter(browser => {
     // Filter tabs that are closing. listTabs calls made right after TabClose
     // events still list tabs in process of being closed.
     let tab = gBrowser.getTabForBrowser(browser);
     return !tab.closing;
   });
 };
 
-BrowserTabList.prototype._isRemoteBrowser = function (browser) {
-  return browser.getAttribute("remote") == "true";
-};
-
 BrowserTabList.prototype.getList = function () {
   let topXULWindow = Services.wm.getMostRecentWindow(
     DebuggerServer.chromeWindowType);
   let selectedBrowser = null;
   if (topXULWindow) {
     selectedBrowser = this._getSelectedBrowser(topXULWindow);
   }
 
@@ -362,27 +358,22 @@ BrowserTabList.prototype.getList = funct
 };
 
 BrowserTabList.prototype._getActorForBrowser = function (browser) {
   // Do we have an existing actor for this browser? If not, create one.
   let actor = this._actorByBrowser.get(browser);
   if (actor) {
     this._foundCount++;
     return actor.update();
-  } else if (this._isRemoteBrowser(browser)) {
-    actor = new RemoteBrowserTabActor(this._connection, browser);
-    this._actorByBrowser.set(browser, actor);
-    this._checkListening();
-    return actor.connect();
   }
 
   actor = new BrowserTabActor(this._connection, browser);
   this._actorByBrowser.set(browser, actor);
   this._checkListening();
-  return promise.resolve(actor);
+  return actor.connect();
 };
 
 BrowserTabList.prototype.getTab = function ({ outerWindowID, tabId }) {
   if (typeof outerWindowID == "number") {
     // First look for in-process frames with this ID
     let window = Services.wm.getOuterWindowWithId(outerWindowID);
     // Safety check to prevent debugging top level window via getTab
     if (window instanceof Ci.nsIDOMChromeWindow) {
@@ -613,18 +604,17 @@ DevToolsUtils.makeInfallible(function (e
     case "TabClose": {
       let actor = this._actorByBrowser.get(browser);
       if (actor) {
         this._handleActorClose(actor, browser);
       }
       break;
     }
     case "TabRemotenessChange": {
-      // We have to remove the cached actor as we have to create a new instance
-      // based on BrowserTabActor or RemoteBrowserTabActor.
+      // We have to remove the cached actor as we have to create a new instance.
       let actor = this._actorByBrowser.get(browser);
       if (actor) {
         this._actorByBrowser.delete(browser);
         // Don't create a new actor; iterate will take care of that. Just notify.
         this._notifyListChanged();
         this._checkListening();
       }
       break;
@@ -841,19 +831,18 @@ exports.BrowserTabList = BrowserTabList;
  * Note that *all* these events are dispatched in the following order
  * when we switch the context of the TabActor to a given iframe:
  *  - will-navigate
  *  - window-destroyed
  *  - changed-toplevel-document
  *  - window-ready
  *  - navigate
  *
- * This class is subclassed by BrowserTabActor and
- * ContentActor. Subclasses are expected to implement a getter
- * for the docShell property.
+ * This class is subclassed by ContentActor and others.
+ * Subclasses are expected to implement a getter for the docShell property.
  *
  * @param connection DebuggerServerConnection
  *        The conection to the client.
  */
 function TabActor(connection) {
   this.conn = connection;
   this._tabActorPool = null;
   // A map of actor names to actor instances provided by extensions.
@@ -2138,137 +2127,54 @@ TabActor.prototype.requestTypes = {
   "listFrames": TabActor.prototype.onListFrames,
   "listWorkers": TabActor.prototype.onListWorkers,
   "resolveLocation": TabActor.prototype.onResolveLocation
 };
 
 exports.TabActor = TabActor;
 
 /**
- * Creates a tab actor for handling requests to a single in-process
- * <xul:browser> tab, or <html:iframe>.
- * Most of the implementation comes from TabActor.
+ * Creates a tab actor for handling requests to a single browser frame.
+ * Both <xul:browser> and <iframe mozbrowser> are supported.
+ * This actor is a shim that connects to a ContentActor in a remote browser process.
+ * All RDP packets get forwarded using the message manager.
  *
- * @param connection DebuggerServerConnection
- *        The connection to the client.
- * @param browser browser
- *        The frame instance that contains this tab.
+ * @param connection The main RDP connection.
+ * @param browser <xul:browser> or <iframe mozbrowser> element to connect to.
  */
 function BrowserTabActor(connection, browser) {
-  TabActor.call(this, connection, browser);
-  this._browser = browser;
-  if (typeof browser.getTabBrowser == "function") {
-    this._tabbrowser = browser.getTabBrowser();
-  }
-
-  Object.defineProperty(this, "docShell", {
-    value: this._browser.docShell,
-    configurable: true
-  });
-}
-
-BrowserTabActor.prototype = Object.create(TabActor.prototype);
-
-BrowserTabActor.prototype.constructor = BrowserTabActor;
-
-Object.defineProperty(BrowserTabActor.prototype, "title", {
-  get() {
-    // On Fennec, we can check the session store data for zombie tabs
-    if (this._browser.__SS_restore) {
-      let sessionStore = this._browser.__SS_data;
-      // Get the last selected entry
-      let entry = sessionStore.entries[sessionStore.index - 1];
-      return entry.title;
-    }
-    let title = this.contentDocument.title || this._browser.contentTitle;
-    // If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
-    // tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
-    // as the title.
-    if (!title && this._tabbrowser) {
-      let tab = this._tabbrowser._getTabForContentWindow(this.window);
-      if (tab) {
-        title = tab.label;
-      }
-    }
-    return title;
-  },
-  enumerable: true,
-  configurable: false
-});
-
-Object.defineProperty(BrowserTabActor.prototype, "url", {
-  get() {
-    // On Fennec, we can check the session store data for zombie tabs
-    if (this._browser.__SS_restore) {
-      let sessionStore = this._browser.__SS_data;
-      // Get the last selected entry
-      let entry = sessionStore.entries[sessionStore.index - 1];
-      return entry.url;
-    }
-    if (this.webNavigation.currentURI) {
-      return this.webNavigation.currentURI.spec;
-    }
-    return null;
-  },
-  enumerable: true,
-  configurable: true
-});
-
-Object.defineProperty(BrowserTabActor.prototype, "browser", {
-  get() {
-    return this._browser;
-  },
-  enumerable: true,
-  configurable: false
-});
-
-BrowserTabActor.prototype.disconnect = function () {
-  TabActor.prototype.disconnect.call(this);
-  this._browser = null;
-  this._tabbrowser = null;
-};
-
-BrowserTabActor.prototype.exit = function () {
-  TabActor.prototype.exit.call(this);
-  this._browser = null;
-  this._tabbrowser = null;
-};
-
-exports.BrowserTabActor = BrowserTabActor;
-
-/**
- * This actor is a shim that connects to a ContentActor in a remote
- * browser process. All RDP packets get forwarded using the message
- * manager.
- *
- * @param connection The main RDP connection.
- * @param browser XUL <browser> element to connect to.
- */
-function RemoteBrowserTabActor(connection, browser) {
   this._conn = connection;
   this._browser = browser;
   this._form = null;
 }
 
-RemoteBrowserTabActor.prototype = {
+BrowserTabActor.prototype = {
   connect() {
     let onDestroy = () => {
       this._form = null;
     };
-    let connect = DebuggerServer.connectToChild(
-      this._conn, this._browser, onDestroy);
+    let connect = DebuggerServer.connectToChild(this._conn, this._browser, onDestroy);
     return connect.then(form => {
       this._form = form;
       return this;
     });
   },
 
+  get _tabbrowser() {
+    if (typeof this._browser.getTabBrowser == "function") {
+      return this._browser.getTabBrowser();
+    }
+    return null;
+  },
+
   get _mm() {
-    return this._browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
-           .messageManager;
+    // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+    // or else fallback to asking the frameLoader itself.
+    return this._browser.messageManager ||
+           this._browser.frameLoader.messageManager;
   },
 
   update() {
     // If the child happens to be crashed/close/detach, it won't have _form set,
     // so only request form update if some code is still listening on the other
     // side.
     if (this._form) {
       let deferred = promise.defer();
@@ -2284,26 +2190,75 @@ RemoteBrowserTabActor.prototype = {
       this._mm.addMessageListener("debug:form", onFormUpdate);
       this._mm.sendAsyncMessage("debug:form");
       return deferred.promise;
     }
 
     return this.connect();
   },
 
+  /**
+   * If we don't have a title from the content side because it's a zombie tab, try to find
+   * it on the chrome side.
+   */
+  get title() {
+    // On Fennec, we can check the session store data for zombie tabs
+    if (this._browser.__SS_restore) {
+      let sessionStore = this._browser.__SS_data;
+      // Get the last selected entry
+      let entry = sessionStore.entries[sessionStore.index - 1];
+      return entry.title;
+    }
+    // If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
+    // tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
+    // as the title.
+    if (this._tabbrowser) {
+      let tab = this._tabbrowser.getTabForBrowser(this._browser);
+      if (tab) {
+        return tab.label;
+      }
+    }
+    return "";
+  },
+
+  /**
+   * If we don't have a url from the content side because it's a zombie tab, try to find
+   * it on the chrome side.
+   */
+  get url() {
+    // On Fennec, we can check the session store data for zombie tabs
+    if (this._browser.__SS_restore) {
+      let sessionStore = this._browser.__SS_data;
+      // Get the last selected entry
+      let entry = sessionStore.entries[sessionStore.index - 1];
+      return entry.url;
+    }
+    return null;
+  },
+
   form() {
-    return this._form;
+    let form = Object.assign({}, this._form);
+    // In some cases, the title and url fields might be empty.  Zombie tabs (not yet
+    // restored) are a good example.  In such cases, try to look up values for these
+    // fields using other data in the parent process.
+    if (!form.title) {
+      form.title = this.title;
+    }
+    if (!form.url) {
+      form.url = this.url;
+    }
+    return form;
   },
 
   exit() {
     this._browser = null;
   },
 };
 
-exports.RemoteBrowserTabActor = RemoteBrowserTabActor;
+exports.BrowserTabActor = BrowserTabActor;
 
 function BrowserAddonList(connection) {
   this._connection = connection;
   this._actorByAddonId = new Map();
   this._onListChanged = null;
 }
 
 BrowserAddonList.prototype.getList = function () {
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -603,17 +603,19 @@ WebConsoleActor.prototype =
         case "NetworkActivity":
           if (!this.networkMonitor) {
             // Create a StackTraceCollector that's going to be shared both by the
             // NetworkMonitorChild (getting messages about requests from parent) and
             // by the NetworkMonitor that directly watches service workers requests.
             this.stackTraceCollector = new StackTraceCollector({ window, appId });
             this.stackTraceCollector.init();
 
-            if (appId || messageManager) {
+            let processBoundary = Services.appinfo.processType !=
+                                  Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+            if ((appId || messageManager) && processBoundary) {
               // Start a network monitor in the parent process to listen to
               // most requests than happen in parent
               this.networkMonitor =
                 new NetworkMonitorChild(appId, messageManager,
                                         this.parentActor.actorID, this);
               this.networkMonitor.init();
               // Spawn also one in the child to listen to service workers
               this.networkMonitorChild = new NetworkMonitor({ window }, this);
--- a/devtools/server/child.js
+++ b/devtools/server/child.js
@@ -1,32 +1,33 @@
 /* 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/. */
 
 "use strict";
 
-/* global addMessageListener, removeMessageListener, sendAsyncMessage */
+/* global addEventListener, addMessageListener, removeMessageListener, sendAsyncMessage */
 
 try {
   var chromeGlobal = this;
 
   // Encapsulate in its own scope to allows loading this frame script more than once.
   (function () {
-    let Cu = Components.utils;
-    let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const Cu = Components.utils;
+    const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+
     const DevToolsUtils = require("devtools/shared/DevToolsUtils");
     const { dumpn } = DevToolsUtils;
     const { DebuggerServer, ActorPool } = require("devtools/server/main");
 
-    // Note that this frame script may be evaluated in non-e10s build. In such case,
-    // DebuggerServer is already going to be initialized.
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
-      DebuggerServer.isInChildProcess = true;
+      // For non-e10s mode, there is only one server instance, so be sure the browser
+      // actors get loaded.
+      DebuggerServer.addBrowserActors();
     }
 
     // In case of apps being loaded in parent process, DebuggerServer is already
     // initialized, but child specific actors are not registered. Otherwise, for apps in
     // child process, we need to load actors the first time we load child.js.
     DebuggerServer.addChildActors();
 
     let connections = new Map();
@@ -93,16 +94,26 @@ try {
       let conn = connections.get(prefix);
       if (conn) {
         conn.close();
         connections.delete(prefix);
       }
     });
     addMessageListener("debug:disconnect", onDisconnect);
 
+    // In non-e10s mode, the "debug:disconnect" message isn't always received before the
+    // messageManager connection goes away.  Watching for "unload" here ensures we close
+    // any connections when the frame is unloaded.
+    addEventListener("unload", () => {
+      for (let conn of connections.values()) {
+        conn.close();
+      }
+      connections.clear();
+    });
+
     let onInspect = DevToolsUtils.makeInfallible(function (msg) {
       // Store the node to be inspected in a global variable (gInspectingNode). Later
       // we'll fetch this variable again using the findInspectingNode request over the
       // remote debugging protocol.
       let inspector = require("devtools/server/actors/inspector");
       inspector.setInspectingNode(msg.objects.node);
     });
     addMessageListener("debug:inspect", onInspect);
--- a/devtools/server/content-server.jsm
+++ b/devtools/server/content-server.jsm
@@ -18,17 +18,16 @@ function init(msg) {
   // the debugger with all devtools modules, nor break the debugger itself with using it
   // in the same process.
   let devtools = new DevToolsLoader();
   devtools.invisibleToDebugger = true;
   let { DebuggerServer, ActorPool } = devtools.require("devtools/server/main");
 
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
-    DebuggerServer.isInChildProcess = true;
   }
 
   // In case of apps being loaded in parent process, DebuggerServer is already
   // initialized, but child specific actors are not registered.
   // Otherwise, for child process, we need to load actors the first
   // time we load child.js
   DebuggerServer.addChildActors();
 
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -891,39 +891,37 @@ var DebuggerServer = {
           });
         }
       };
       dbg.addListener(listener);
     });
   },
 
   /**
-   * Check if the caller is running in a content child process.
-   * (Eventually set by child.js)
-   *
-   * @return boolean
-   *         true if the caller is running in a content
+   * Check if the server is running in the child process.
    */
-  isInChildProcess: false,
+  get isInChildProcess() {
+    return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+  },
 
   /**
    * In a chrome parent process, ask all content child processes
    * to execute a given module setup helper.
    *
    * @param module
    *        The module to be required
    * @param setupChild
    *        The name of the setup helper exported by the above module
    *        (setup helper signature: function ({mm}) { ... })
    * @param waitForEval (optional)
    *        If true, the returned promise only resolves once code in child
    *        is evaluated
    */
   setupInChild({ module, setupChild, args, waitForEval }) {
-    if (this.isInChildProcess || this._childMessageManagers.size == 0) {
+    if (this._childMessageManagers.size == 0) {
       return Promise.resolve();
     }
     let deferred = Promise.defer();
 
     // If waitForEval is set, pass a unique id and expect child.js to send
     // a message back once the code in child is evaluated.
     if (typeof (waitForEval) != "boolean") {
       waitForEval = false;
@@ -977,17 +975,19 @@ var DebuggerServer = {
    *        related TabActor)
    * @return object
    *         A promise object that is resolved once the connection is
    *         established.
    */
   connectToChild(connection, frame, onDestroy) {
     let deferred = SyncPromise.defer();
 
-    let mm = frame.frameLoader.messageManager;
+    // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+    // or else fallback to asking the frameLoader itself.
+    let mm = frame.messageManager || frame.frameLoader.messageManager;
     mm.loadFrameScript("resource://devtools/server/child.js", false);
     this._childMessageManagers.add(mm);
 
     let actor, childTransport;
     let prefix = connection.allocID("child");
     let netMonitor = null;
 
     // provides hook to actor modules that need to exchange messages
@@ -1293,17 +1293,32 @@ var DebuggerServer = {
       if ((handler.name && handler.name == actor.name) ||
           (handler.id && handler.id == actor.id)) {
         delete DebuggerServer.globalActorFactories[name];
         for (let connID of Object.getOwnPropertyNames(this._connections)) {
           this._connections[connID].rootActor.removeActorByName(name);
         }
       }
     }
-  }
+  },
+
+  /**
+   * ⚠ TESTING ONLY! ⚠ Searches all active connections for an actor matching an ID.
+   * This is helpful for some tests which depend on reaching into the server to check some
+   * properties of an actor.
+   */
+  _searchAllConnectionsForActor(actorID) {
+    for (let connID of Object.getOwnPropertyNames(this._connections)) {
+      let actor = this._connections[connID].getActor(actorID);
+      if (actor) {
+        return actor;
+      }
+    }
+    return null;
+  },
 };
 
 // Expose these to save callers the trouble of importing DebuggerSocket
 DevToolsUtils.defineLazyGetter(DebuggerServer, "Authenticators", () => {
   return Authentication.Authenticators;
 });
 DevToolsUtils.defineLazyGetter(DebuggerServer, "AuthenticationResult", () => {
   return Authentication.AuthenticationResult;
@@ -1385,17 +1400,19 @@ DebuggerServerConnection.prototype = {
   /**
    * Message manager used to communicate with the parent process,
    * set by child.js. Is only defined for connections instantiated
    * within a child process.
    */
   parentMessageManager: null,
 
   close() {
-    this._transport.close();
+    if (this._transport) {
+      this._transport.close();
+    }
   },
 
   send(packet) {
     this.transport.send(packet);
   },
 
   /**
    * Used when sending a bulk reply from an actor.
@@ -1589,16 +1606,23 @@ DebuggerServerConnection.prototype = {
   },
 
   /*
    * Stop forwarding messages to actors whose names begin with
    * |prefix+'/'|. Such messages will now elicit 'noSuchActor' errors.
    */
   cancelForwarding(prefix) {
     this._forwardingPrefixes.delete(prefix);
+
+    // Notify the client that forwarding in now cancelled for this prefix.
+    // There could be requests in progress that the client should abort rather leaving
+    // handing indefinitely.
+    if (this.rootActor) {
+      this.send(this.rootActor.forwardingCancelled(prefix));
+    }
   },
 
   sendActorEvent(actorID, eventName, event = {}) {
     event.from = actorID;
     event.type = eventName;
     this.send(event);
   },
 
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -62,19 +62,19 @@ skip-if = e10s # Bug 1183605 - devtools/
 [browser_markers-styles.js]
 [browser_markers-timestamp.js]
 [browser_navigateEvents.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-allocation-data.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-profiler-01.js]
 [browser_perf-profiler-02.js]
-skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
+skip-if = true # Needs to be updated for async actor destruction
 [browser_perf-profiler-03.js]
-skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
+skip-if = true # Needs to be updated for async actor destruction
 [browser_perf-realtime-markers.js]
 [browser_perf-recording-actor-01.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-recording-actor-02.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-samples-01.js]
 [browser_perf-samples-02.js]
 #[browser_perf-front-profiler-01.js] bug 1077464
--- a/devtools/server/tests/browser/browser_navigateEvents.js
+++ b/devtools/server/tests/browser/browser_navigateEvents.js
@@ -27,26 +27,26 @@ function assertEvent(event, data) {
     case x++:
       is(event, "unload-dialog", "We get the dialog on first page unload");
       break;
     case x++:
       is(event, "will-navigate", "The very first event is will-navigate on server side");
       is(data.newURI, URL2, "newURI property is correct");
       break;
     case x++:
-      is(event, "tabNavigated", "Right after will-navigate, the client receive tabNavigated");
+      is(event, "request", "RDP is async with messageManager, the request happens after will-navigate");
+      is(data, URL2);
+      break;
+    case x++:
+      is(event, "tabNavigated", "After the request, the client receive tabNavigated");
       is(data.state, "start", "state is start");
       is(data.url, URL2, "url property is correct");
       is(data.nativeConsoleAPI, true, "nativeConsoleAPI is correct");
       break;
     case x++:
-      is(event, "request", "Given that locally, the Debugger protocol is sync, the request happens after tabNavigated");
-      is(data, URL2);
-      break;
-    case x++:
       is(event, "DOMContentLoaded");
       is(content.document.readyState, "interactive");
       break;
     case x++:
       is(event, "load");
       is(content.document.readyState, "complete");
       break;
     case x++:
@@ -101,18 +101,17 @@ function getServerTabActor(callback) {
 
   // Connect to this tab
   let transport = DebuggerServer.connectPipe();
   client = new DebuggerClient(transport);
   connectDebuggerClient(client).then(form => {
     let actorID = form.actor;
     client.attachTab(actorID, function (aResponse, aTabClient) {
       // !Hack! Retrieve a server side object, the BrowserTabActor instance
-      let conn = transport._serverConnection;
-      let tabActor = conn.getActor(actorID);
+      let tabActor = DebuggerServer._searchAllConnectionsForActor(actorID);
       callback(tabActor);
     });
   });
 
   client.addListener("tabNavigated", function (aEvent, aPacket) {
     assertEvent("tabNavigated", aPacket);
   });
 }
--- a/devtools/server/tests/browser/head.js
+++ b/devtools/server/tests/browser/head.js
@@ -56,17 +56,19 @@ function* initAnimationsFrontForUrl(url)
   return {inspector, walker, animations, client};
 }
 
 function initDebuggerServer() {
   try {
     // Sometimes debugger server does not get destroyed correctly by previous
     // tests.
     DebuggerServer.destroy();
-  } catch (ex) { }
+  } catch (e) {
+    info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
+  }
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 }
 
 /**
  * Connect a debugger client.
  * @param {DebuggerClient}
  * @return {Promise} Resolves to the selected tabActor form when the client is
--- a/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/devtools/server/tests/mochitest/inspector-helpers.js
@@ -116,18 +116,17 @@ function serverOwnershipSubtree(walker, 
   }
   return {
     name: actor.actorID,
     children: sortOwnershipChildren(children)
   };
 }
 
 function serverOwnershipTree(walker) {
-  let serverConnection = walker.conn._transport._serverConnection;
-  let serverWalker = serverConnection.getActor(walker.actorID);
+  let serverWalker = DebuggerServer._searchAllConnectionsForActor(walker.actorID);
 
   return {
     root: serverOwnershipSubtree(serverWalker, serverWalker.rootDoc),
     orphaned: [...serverWalker._orphaned].map(o => serverOwnershipSubtree(serverWalker, o.rawNode)),
     retained: [...serverWalker._retainedOrphans].map(o => serverOwnershipSubtree(serverWalker, o.rawNode))
   };
 }
 
--- a/devtools/server/tests/mochitest/test_animation_actor-lifetime.html
+++ b/devtools/server/tests/mochitest/test_animation_actor-lifetime.html
@@ -42,18 +42,17 @@ window.onload = function() {
 
   addAsyncTest(function* testActorLifetime() {
 
     info ("Testing animated node actor");
     let animatedNodeActor = yield gWalker.querySelector(gWalker.rootNode,
       ".animated");
     yield animationsFront.getAnimationPlayersForNode(animatedNodeActor);
 
-    let serverConnection = animationsFront.conn._transport._serverConnection;
-    let animationsActor = serverConnection.getActor(animationsFront.actorID);
+    let animationsActor = DebuggerServer._searchAllConnectionsForActor(animationsFront.actorID);
 
     is(animationsActor.actors.length, 1,
       "AnimationActor have 1 AnimationPlayerActors");
 
     info ("Testing AnimationPlayerActors release");
     let stillNodeActor = yield gWalker.querySelector(gWalker.rootNode,
       ".still");
     yield animationsFront.getAnimationPlayersForNode(stillNodeActor);
--- a/devtools/server/tests/mochitest/test_inspector-anonymous.html
+++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html
@@ -67,18 +67,17 @@ window.onload = function() {
     is (children.nodes.length, 2, "No native anon content for form control");
 
     runNextTest();
   });
 
   addAsyncTest(function* testNativeAnonymousStartingNode() {
     info ("Tests attaching an element that a walker can't see.");
 
-    let serverConnection = gWalker.conn._transport._serverConnection;
-    let serverWalker = serverConnection.getActor(gWalker.actorID);
+    let serverWalker = DebuggerServer._searchAllConnectionsForActor(gWalker.actorID);
     let docwalker = new _documentWalker(
       gInspectee.querySelector("select"),
       gInspectee.defaultView,
       nodeFilterConstants.SHOW_ALL,
       () => {
         return nodeFilterConstants.FILTER_ACCEPT
       }
     );
--- a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
+++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
@@ -37,314 +37,336 @@ addAsyncTest(function() {
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.parents(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.parents(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.parents() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.children(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.children(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.children() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.siblings(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.siblings(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.siblings() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.nextSibling(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.nextSibling(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.nextSibling() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.previousSibling(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.previousSibling(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.previousSibling() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.addPseudoClassLock(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.addPseudoClassLock(nodeFront, ":hover");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.addPseudoClassLock() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removePseudoClassLock(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.removePseudoClassLock(nodeFront, ":hover");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.removePseudoClassLock() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.clearPseudoClassLocks(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.clearPseudoClassLocks(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.clearPseudoClassLocks() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.innerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.innerHTML(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.innerHTML() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.setInnerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.setInnerHTML(nodeFront, "<span>innerHTML changed</span>");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.setInnerHTML() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.outerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.outerHTML(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.outerHTML() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.setOuterHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.setOuterHTML(nodeFront, "<h1><span>innerHTML changed</span></h1>");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.setOuterHTML() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertAdjacentHTML(nodeFront) before the load completes shouldn't " +
     "fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertAdjacentHTML(nodeFront, "afterEnd",
     "<span>new adjacent HTML</span>");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.insertAdjacentHTML() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removeNode(nodeFront) before the load completes should throw");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   let hasThrown = false;
   try {
     yield gWalker.removeNode(nodeFront);
   } catch (e) {
     hasThrown = true;
   }
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(hasThrown, "The call to walker.removeNode() threw");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removeNodes([nodeFront]) before the load completes should throw");
 
   let nodeFront1 = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let nodeFront2 = yield gWalker.querySelector(gWalker.rootNode, "#longstring");
   let nodeFront3 = yield gWalker.querySelector(gWalker.rootNode, "#shortstring");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   let hasThrown = false;
   try {
     yield gWalker.removeNodes([nodeFront1, nodeFront2, nodeFront3]);
   } catch (e) {
     hasThrown = true;
   }
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(hasThrown, "The call to walker.removeNodes() threw");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertBefore(nodeFront, parent, null) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertBefore(nodeFront, newParentFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.insertBefore() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertBefore(nodeFront, parent, sibling) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist");
   let siblingFront = yield gWalker.querySelector(gWalker.rootNode, "#b");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertBefore(nodeFront, newParentFront, siblingFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.insertBefore() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.editTagName(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.editTagName(nodeFront, "h2");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.editTagName() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.hideNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.hideNode(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.hideNode() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.unhideNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.unhideNode(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.unhideNode() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.releaseNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.releaseNode(nodeFront);
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.releaseNode() didn't fail");
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.querySelector(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body");
+  let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.querySelector(nodeFront, "h1");
-  yield waitForMutation(gWalker, isNewRoot);
+  yield newRoot;
 
   ok(true, "The call to walker.querySelector() didn't fail");
   runNextTest();
 });
 
 addTest(function cleanup() {
   gWalker = gDoc = null;
   runNextTest();
--- a/devtools/server/tests/mochitest/test_inspector-search.html
+++ b/devtools/server/tests/mochitest/test_inspector-search.html
@@ -41,18 +41,17 @@ window.onload = function() {
         inspector = InspectorFront(client, tab);
         resolve();
       });
     });
 
     let walkerFront = yield inspector.getWalker();
     ok(walkerFront, "getWalker() should return an actor.");
 
-    let serverConnection = walkerFront.conn._transport._serverConnection;
-    walkerActor = serverConnection.getActor(walkerFront.actorID);
+    walkerActor = DebuggerServer._searchAllConnectionsForActor(walkerFront.actorID);
     ok(walkerActor,
       "Got a reference to the walker actor (" + walkerFront.actorID + ")");
 
     walkerSearch = walkerActor.walkerSearch;
 
     runNextTest();
   });
 
--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
+++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
@@ -35,17 +35,17 @@ window.onload = function() {
 }
 
 function runTests() {
   // Create a minimal iframe with a message manager
   let iframe = document.createElement("iframe");
   iframe.mozbrowser = true;
   document.body.appendChild(iframe);
 
-  let mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
+  let mm = iframe.frameLoader.messageManager;
 
   // Instantiate a minimal server
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
   }
   if (!DebuggerServer.createRootActor) {
     DebuggerServer.addBrowserActors();
   }
--- a/devtools/server/tests/unit/test_client_request.js
+++ b/devtools/server/tests/unit/test_client_request.js
@@ -151,17 +151,17 @@ function test_close_client_while_sending
   let pendingRequest = gClient.request({
     to: gActorId,
     type: "hello"
   });
 
   let expectReply = promise.defer();
   gClient.expectReply("root", function (response) {
     do_check_eq(response.error, "connectionClosed");
-    do_check_eq(response.message, "server side packet from 'root' can't be received as the connection just closed.");
+    do_check_eq(response.message, "server side packet can't be received as the connection just closed.");
     expectReply.resolve();
   });
 
   gClient.close(() => {
     activeRequest.then(() => {
       ok(false, "First request unexpectedly succeed while closing the connection");
     }, response => {
       do_check_eq(response.error, "connectionClosed");
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -937,16 +937,26 @@ DebuggerClient.prototype = {
     // If we have a registered Front for this actor, let it handle the packet
     // and skip all the rest of this unpleasantness.
     let front = this.getActor(aPacket.from);
     if (front) {
       front.onPacket(aPacket);
       return;
     }
 
+    // Check for "forwardingCancelled" here instead of using a client to handle it.
+    // This is necessary because we might receive this event while the client is closing,
+    // and the clients have already been removed by that point.
+    if (this.mainRoot &&
+        aPacket.from == this.mainRoot.actor &&
+        aPacket.type == "forwardingCancelled") {
+      this.purgeRequests(aPacket.prefix);
+      return;
+    }
+
     if (this._clients.has(aPacket.from) && aPacket.type) {
       let client = this._clients.get(aPacket.from);
       let type = aPacket.type;
       if (client.events.indexOf(type) != -1) {
         client.emit(type, aPacket);
         // we ignore the rest, as the client is expected to handle this packet.
         return;
       }
@@ -1067,45 +1077,21 @@ DebuggerClient.prototype = {
 
   /**
    * Called by DebuggerTransport when the underlying stream is closed.
    *
    * @param aStatus nsresult
    *        The status code that corresponds to the reason for closing
    *        the stream.
    */
-  onClosed: function (aStatus) {
+  onClosed: function () {
     this._closed = true;
     this.emit("closed");
 
-    // Reject all pending and active requests
-    let reject = function (type, request, actor) {
-      // Server can send packets on its own and client only pass a callback
-      // to expectReply, so that there is no request object.
-      let msg;
-      if (request.request) {
-        msg = "'" + request.request.type + "' " + type + " request packet" +
-              " to '" + actor + "' " +
-              "can't be sent as the connection just closed.";
-      } else {
-        msg = "server side packet from '" + actor + "' can't be received " +
-              "as the connection just closed.";
-      }
-      let packet = { error: "connectionClosed", message: msg };
-      request.emit("json-reply", packet);
-    };
-
-    let pendingRequests = new Map(this._pendingRequests);
-    this._pendingRequests.clear();
-    pendingRequests.forEach((list, actor) => {
-      list.forEach(request => reject("pending", request, actor));
-    });
-    let activeRequests = new Map(this._activeRequests);
-    this._activeRequests.clear();
-    activeRequests.forEach(reject.bind(null, "active"));
+    this.purgeRequests();
 
     // The |_pools| array on the client-side currently is used only by
     // protocol.js to store active fronts, mirroring the actor pools found in
     // the server.  So, read all usages of "pool" as "protocol.js front".
     //
     // In the normal case where we shutdown cleanly, the toolbox tells each tool
     // to close, and they each call |destroy| on any fronts they were using.
     // When |destroy| or |cleanup| is called on a protocol.js front, it also
@@ -1119,16 +1105,61 @@ DebuggerClient.prototype = {
     // from |_pools|.  This saves the toolbox from hanging indefinitely, in case
     // it waits for some server response before shutdown that will now never
     // arrive.
     for (let pool of this._pools) {
       pool.cleanup();
     }
   },
 
+  /**
+   * Purge pending and active requests in this client.
+   *
+   * @param prefix string (optional)
+   *        If a prefix is given, only requests for actor IDs that start with the prefix
+   *        will be cleaned up.  This is useful when forwarding of a portion of requests
+   *        is cancelled on the server.
+   */
+  purgeRequests(prefix = "") {
+    let reject = function (type, request) {
+      // Server can send packets on its own and client only pass a callback
+      // to expectReply, so that there is no request object.
+      let msg;
+      if (request.request) {
+        msg = "'" + request.request.type + "' " + type + " request packet" +
+              " to '" + request.actor + "' " +
+              "can't be sent as the connection just closed.";
+      } else {
+        msg = "server side packet can't be received as the connection just closed.";
+      }
+      let packet = { error: "connectionClosed", message: msg };
+      request.emit("json-reply", packet);
+    };
+
+    let pendingRequestsToReject = [];
+    this._pendingRequests.forEach((requests, actor) => {
+      if (!actor.startsWith(prefix)) {
+        return;
+      }
+      this._pendingRequests.delete(actor);
+      pendingRequestsToReject = pendingRequestsToReject.concat(requests);
+    });
+    pendingRequestsToReject.forEach(request => reject("pending", request));
+
+    let activeRequestsToReject = [];
+    this._activeRequests.forEach((request, actor) => {
+      if (!actor.startsWith(prefix)) {
+        return;
+      }
+      this._activeRequests.delete(actor);
+      activeRequestsToReject = activeRequestsToReject.concat(request);
+    });
+    activeRequestsToReject.forEach(request => reject("active", request));
+  },
+
   registerClient: function (client) {
     let actorID = client.actor;
     if (!actorID) {
       throw new Error("DebuggerServer.registerClient expects " +
                       "a client instance with an `actor` attribute.");
     }
     if (!Array.isArray(client.events)) {
       throw new Error("DebuggerServer.registerClient expects " +
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -419,21 +419,22 @@ const NodeFront = FrontClassWithSpec(nod
   },
 
   /**
    * Get an nsIDOMNode for the given node front.  This only works locally,
    * and is only intended as a stopgap during the transition to the remote
    * protocol.  If you depend on this you're likely to break soon.
    */
   rawNode: function (rawNode) {
-    if (!this.conn._transport._serverConnection) {
+    if (!this.isLocalToBeDeprecated()) {
       console.warn("Tried to use rawNode on a remote connection.");
       return null;
     }
-    let actor = this.conn._transport._serverConnection.getActor(this.actorID);
+    const { DebuggerServer } = require("devtools/server/main");
+    let actor = DebuggerServer._searchAllConnectionsForActor(this.actorID);
     if (!actor) {
       // Can happen if we try to get the raw node for an already-expired
       // actor.
       return null;
     }
     return actor.rawNode;
   }
 });
@@ -890,18 +891,18 @@ const WalkerFront = FrontClassWithSpec(w
 
   // XXX hack during transition to remote inspector: get a proper NodeFront
   // for a given local node.  Only works locally.
   frontForRawNode: function (rawNode) {
     if (!this.isLocal()) {
       console.warn("Tried to use frontForRawNode on a remote connection.");
       return null;
     }
-    let walkerActor = this.conn._transport._serverConnection
-      .getActor(this.actorID);
+    const { DebuggerServer } = require("devtools/server/main");
+    let walkerActor = DebuggerServer._searchAllConnectionsForActor(this.actorID);
     if (!walkerActor) {
       throw Error("Could not find client side for actor " + this.actorID);
     }
     let nodeActor = walkerActor._ref(rawNode);
 
     // Pass the node through a read/write pair to create the client side actor.
     let nodeType = types.getType("domnode");
     let returnNode = nodeType.read(
@@ -911,17 +912,17 @@ const WalkerFront = FrontClassWithSpec(w
     for (let extraActor of extras) {
       top = nodeType.read(nodeType.write(extraActor, walkerActor), this);
     }
 
     if (top !== this.rootNode) {
       // Imported an already-orphaned node.
       this._orphaned.add(top);
       walkerActor._orphaned
-        .add(this.conn._transport._serverConnection.getActor(top.actorID));
+        .add(DebuggerServer._searchAllConnectionsForActor(top.actorID));
     }
     return returnNode;
   },
 
   removeNode: custom(Task.async(function* (node) {
     let previousSibling = yield this.previousSibling(node);
     let nextSibling = yield this._removeNode(node);
     return {
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -17,17 +17,17 @@
     factory.call(this, require, this);
   } else {
     // Cu.import
     const Cu = Components.utils;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     factory.call(this, require, this);
   }
 }).call(this, function (require, exports) {
-  const { Cc, Ci, Cr, CC } = require("chrome");
+  const { Cc, Cr, CC } = require("chrome");
   const DevToolsUtils = require("devtools/shared/DevToolsUtils");
   const { dumpn, dumpv } = DevToolsUtils;
   const StreamUtils = require("devtools/shared/transport/stream-utils");
   const { Packet, JSONPacket, BulkPacket } =
   require("devtools/shared/transport/packets");
   const promise = require("promise");
   const defer = require("devtools/shared/defer");
   const EventEmitter = require("devtools/shared/event-emitter");
@@ -709,17 +709,17 @@
    * multiple servers running in the same child process.
    *
    * This transport exchanges messages named 'debug:<prefix>:packet', where
    * <prefix> is |prefix|, whose data is the protocol packet.
    */
   function ChildDebuggerTransport(sender, prefix) {
     EventEmitter.decorate(this);
 
-    this._sender = sender.QueryInterface(Ci.nsIMessageSender);
+    this._sender = sender;
     this._messageName = "debug:" + prefix + ":packet";
   }
 
   /*
    * To avoid confusion, we use 'message' to mean something that
    * nsIMessageSender conveys, and 'packet' to mean a remote debugging
    * protocol packet.
    */
@@ -728,29 +728,47 @@
 
     hooks: null,
 
     ready: function () {
       this._sender.addMessageListener(this._messageName, this);
     },
 
     close: function () {
-      this._sender.removeMessageListener(this._messageName, this);
+      try {
+        this._sender.removeMessageListener(this._messageName, this);
+      } catch (e) {
+        if (e.result != Cr.NS_ERROR_NULL_POINTER) {
+          throw e;
+        }
+        // In some cases, especially when using messageManagers in non-e10s mode, we reach
+        // this point with a dead messageManager which only throws errors but does not
+        // seem to indicate in any other way that it is dead.
+      }
       this.emit("onClosed");
       this.hooks.onClosed();
     },
 
     receiveMessage: function ({data}) {
       this.emit("onPacket", data);
       this.hooks.onPacket(data);
     },
 
     send: function (packet) {
       this.emit("send", packet);
-      this._sender.sendAsyncMessage(this._messageName, packet);
+      try {
+        this._sender.sendAsyncMessage(this._messageName, packet);
+      } catch (e) {
+        if (e.result != Cr.NS_ERROR_NULL_POINTER) {
+          throw e;
+        }
+        // In some cases, especially when using messageManagers in non-e10s mode, we reach
+        // this point with a dead messageManager which only throws errors but does not
+        // seem to indicate in any other way that it is dead.
+      }
     },
 
     startBulkSend: function () {
       throw new Error("Can't send bulk data to child processes.");
     }
   };
 
   exports.ChildDebuggerTransport = ChildDebuggerTransport;
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -76,18 +76,35 @@ function matchRequest(channel, filters) 
         break;
       }
       win = win.parent;
     }
   }
 
   if (filters.topFrame) {
     let topFrame = NetworkHelper.getTopFrameForRequest(channel);
-    if (topFrame && topFrame === filters.topFrame) {
-      return true;
+    while (topFrame) {
+      // In the normal case, a topFrame filter should match the request's topFrame if it
+      // will match at all.
+      if (topFrame === filters.topFrame) {
+        return true;
+      }
+      // As a stop gap approach for RDM, where `filters.topFrame` will be the
+      // <xul:browser> frame for an entire tab and the request's `topFrame` is the
+      // <iframe mozbrower> that triggered the request, we try to climb up parent frames
+      // above the request's `topFrame` to see if they might also match the filter.
+      // In bug 1240912, we want to rework this, since we don't really want to be passing
+      // a frame down to the network monitor.
+      if (!topFrame.ownerGlobal) {
+        break;
+      }
+      topFrame = topFrame.ownerGlobal
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils)
+                         .containerElement;
     }
   }
 
   if (filters.appId) {
     let appId = NetworkHelper.getAppIdForRequest(channel);
     if (appId && appId == filters.appId) {
       return true;
     }
@@ -1578,18 +1595,19 @@ NetworkEventActorProxy.prototype = {
  * @constructor
  * @param nsIDOMElement frame
  *        The browser frame to work with (mozbrowser).
  * @param string id
  *        Instance identifier to use for messages.
  */
 function NetworkMonitorManager(frame, id) {
   this.id = id;
-  let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
-      .messageManager;
+  // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+  // or else fallback to asking the frameLoader itself.
+  let mm = frame.messageManager || frame.frameLoader.messageManager;
   this.messageManager = mm;
   this.frame = frame;
   this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
   this.onNetworkEvent = this.onNetworkEvent.bind(this);
 
   mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
 }
 exports.NetworkMonitorManager = NetworkMonitorManager;
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -604,17 +604,17 @@ KeyframeEffectReadOnly::UpdateProperties
 
   for (AnimationProperty& property : mProperties) {
     property.mWinsInCascade =
       winningInCascadeProperties.HasProperty(property.mProperty);
     property.mIsRunningOnCompositor =
       runningOnCompositorProperties.HasProperty(property.mProperty);
   }
 
-  CalculateCumulativeChangeHint();
+  CalculateCumulativeChangeHint(aStyleContext);
 
   if (mTarget) {
     EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
                                                    mTarget->mPseudoType);
     if (effectSet) {
       effectSet->MarkCascadeNeedsUpdate();
     }
 
@@ -1466,24 +1466,71 @@ KeyframeEffectReadOnly::SetPerformanceWa
         nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
         AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget->mElement);
       }
       return;
     }
   }
 }
 
+static already_AddRefed<nsStyleContext>
+CreateStyleContextForAnimationValue(nsCSSProperty aProperty,
+                                    StyleAnimationValue aValue,
+                                    nsStyleContext* aBaseStyleContext)
+{
+  MOZ_ASSERT(aBaseStyleContext,
+             "CreateStyleContextForAnimationValue needs to be called "
+             "with a valid nsStyleContext");
+
+  RefPtr<AnimValuesStyleRule> styleRule = new AnimValuesStyleRule();
+  styleRule->AddValue(aProperty, aValue);
+
+  nsCOMArray<nsIStyleRule> rules;
+  rules.AppendObject(styleRule);
+
+  MOZ_ASSERT(aBaseStyleContext->PresContext()->StyleSet()->IsGecko(),
+             "ServoStyleSet should not use StyleAnimationValue for animations");
+  nsStyleSet* styleSet =
+    aBaseStyleContext->PresContext()->StyleSet()->AsGecko();
+
+  RefPtr<nsStyleContext> styleContext =
+    styleSet->ResolveStyleByAddingRules(aBaseStyleContext, rules);
+
+  // We need to call StyleData to generate cached data for the style context.
+  // Otherwise CalcStyleDifference returns no meaningful result.
+  styleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
+
+  return styleContext.forget();
+}
+
 void
-KeyframeEffectReadOnly::CalculateCumulativeChangeHint()
+KeyframeEffectReadOnly::CalculateCumulativeChangeHint(
+  nsStyleContext *aStyleContext)
 {
   mCumulativeChangeHint = nsChangeHint(0);
 
   for (const AnimationProperty& property : mProperties) {
     for (const AnimationPropertySegment& segment : property.mSegments) {
-      mCumulativeChangeHint |= segment.mChangeHint;
+      RefPtr<nsStyleContext> fromContext =
+        CreateStyleContextForAnimationValue(property.mProperty,
+                                            segment.mFromValue, aStyleContext);
+
+      RefPtr<nsStyleContext> toContext =
+        CreateStyleContextForAnimationValue(property.mProperty,
+                                            segment.mToValue, aStyleContext);
+
+      uint32_t equalStructs = 0;
+      uint32_t samePointerStructs = 0;
+      nsChangeHint changeHint =
+        fromContext->CalcStyleDifference(toContext,
+                                         nsChangeHint(0),
+                                         &equalStructs,
+                                         &samePointerStructs);
+
+      mCumulativeChangeHint |= changeHint;
     }
   }
 }
 
 bool
 KeyframeEffectReadOnly::CanIgnoreIfNotVisible() const
 {
   if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -115,18 +115,16 @@ struct Keyframe
 };
 
 struct AnimationPropertySegment
 {
   float mFromKey, mToKey;
   StyleAnimationValue mFromValue, mToValue;
   Maybe<ComputedTimingFunction> mTimingFunction;
 
-  nsChangeHint mChangeHint;
-
   bool operator==(const AnimationPropertySegment& aOther) const {
     return mFromKey == aOther.mFromKey &&
            mToKey == aOther.mToKey &&
            mFromValue == aOther.mFromValue &&
            mToValue == aOther.mToValue &&
            mTimingFunction == aOther.mTimingFunction;
   }
   bool operator!=(const AnimationPropertySegment& aOther) const {
@@ -342,17 +340,17 @@ public:
   // compositor. |aParams| and |aParamsLength| are optional parameters which
   // will be used to generate a localized message for devtools.
   void SetPerformanceWarning(
     nsCSSProperty aProperty,
     const AnimationPerformanceWarning& aWarning);
 
   // Cumulative change hint on each segment for each property.
   // This is used for deciding the animation is paint-only.
-  void CalculateCumulativeChangeHint();
+  void CalculateCumulativeChangeHint(nsStyleContext* aStyleContext);
 
   // Returns true if all of animation properties' change hints
   // can ignore painting if the animation is not visible.
   // See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h
   // in detail which change hint can be ignored.
   bool CanIgnoreIfNotVisible() const;
 
 protected:
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -1069,46 +1069,16 @@ MarkAsComputeValuesFailureKey(PropertyVa
 static bool
 IsComputeValuesFailureKey(const PropertyValuePair& aPair)
 {
   return nsCSSProps::IsShorthand(aPair.mProperty) &&
          aPair.mValue.GetTokenStreamValue()->mPropertyID ==
            eCSSPropertyExtra_no_properties;
 }
 
-static already_AddRefed<nsStyleContext>
-CreateStyleContextForAnimationValue(nsCSSProperty aProperty,
-                                    StyleAnimationValue aValue,
-                                    nsStyleContext* aBaseStyleContext)
-{
-  MOZ_ASSERT(aBaseStyleContext,
-             "CreateStyleContextForAnimationValue needs to be called "
-             "with a valid nsStyleContext");
-
-  RefPtr<AnimValuesStyleRule> styleRule = new AnimValuesStyleRule();
-  styleRule->AddValue(aProperty, aValue);
-
-  nsCOMArray<nsIStyleRule> rules;
-  rules.AppendObject(styleRule);
-
-  MOZ_ASSERT(aBaseStyleContext->PresContext()->StyleSet()->IsGecko(),
-             "ServoStyleSet should not use StyleAnimationValue for animations");
-  nsStyleSet* styleSet =
-    aBaseStyleContext->PresContext()->StyleSet()->AsGecko();
-
-  RefPtr<nsStyleContext> styleContext =
-    styleSet->ResolveStyleByAddingRules(aBaseStyleContext, rules);
-
-  // We need to call StyleData to generate cached data for the style context.
-  // Otherwise CalcStyleDifference returns no meaningful result.
-  styleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
-
-  return styleContext.forget();
-}
-
 /**
  * Builds an array of AnimationProperty objects to represent the keyframe
  * animation segments in aEntries.
  */
 static void
 BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
                               nsTArray<KeyframeValueEntry>& aEntries,
                               nsTArray<AnimationProperty>& aResult)
@@ -1238,32 +1208,16 @@ BuildSegmentsFromValueEntries(nsStyleCon
     AnimationPropertySegment* segment =
       animationProperty->mSegments.AppendElement();
     segment->mFromKey   = aEntries[i].mOffset;
     segment->mToKey     = aEntries[j].mOffset;
     segment->mFromValue = aEntries[i].mValue;
     segment->mToValue   = aEntries[j].mValue;
     segment->mTimingFunction = aEntries[i].mTimingFunction;
 
-    RefPtr<nsStyleContext> fromContext =
-      CreateStyleContextForAnimationValue(animationProperty->mProperty,
-                                          segment->mFromValue, aStyleContext);
-
-    RefPtr<nsStyleContext> toContext =
-      CreateStyleContextForAnimationValue(animationProperty->mProperty,
-                                          segment->mToValue, aStyleContext);
-
-    uint32_t equalStructs = 0;
-    uint32_t samePointerStructs = 0;
-    segment->mChangeHint =
-        fromContext->CalcStyleDifference(toContext,
-          nsChangeHint(0),
-          &equalStructs,
-          &samePointerStructs);
-
     i = j;
   }
 }
 
 /**
  * Converts a JS object representing a property-indexed keyframe into
  * an array of Keyframe objects.
  *
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9234,44 +9234,29 @@ nsContentUtils::GetPresentationURL(nsIDo
   }
 
   topFrameElement->GetAttribute(NS_LITERAL_STRING("mozpresentation"), aPresentationUrl);
 }
 
 /* static */ nsIDocShell*
 nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget)
 {
-  nsCOMPtr<nsINode> node(do_QueryInterface(aTarget));
-  nsIDocument* doc = nullptr;
-  nsIDocShell* docShell = nullptr;
-
-  if (node) {
-    doc = node->OwnerDoc();
-    if (!doc->GetDocShell()) {
-      bool ignore;
-      nsCOMPtr<nsPIDOMWindowInner> window =
-        do_QueryInterface(doc->GetScriptHandlingObject(ignore));
-      if (window) {
-        doc = window->GetExtantDoc();
-      }
-    }
+  nsCOMPtr<nsPIDOMWindowInner> innerWindow;
+
+  if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
+    bool ignore;
+    innerWindow =
+      do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
+  } else if ((innerWindow = do_QueryInterface(aTarget))) {
+    // Nothing else to do
   } else {
-    nsCOMPtr<nsPIDOMWindowInner> window(do_QueryInterface(aTarget));
-    if (window) {
-      doc = window->GetExtantDoc();
-    }
-  }
-
-  if (!doc) {
-    nsCOMPtr<DOMEventTargetHelper> helper(do_QueryInterface(aTarget));
+    nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(aTarget);
     if (helper) {
-      if (nsPIDOMWindowInner* window = helper->GetOwner()) {
-        doc = window->GetExtantDoc();
-      }
-    }
-  }
-
-  if (doc) {
-    docShell = doc->GetDocShell();
-  }
-
-  return docShell;
-}
+      innerWindow = helper->GetOwner();
+    }
+  }
+
+  if (innerWindow) {
+    return innerWindow->GetDocShell();
+  }
+
+  return nullptr;
+}
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1658,29 +1658,26 @@ NS_NewGlobalMessageManager(nsIMessageBro
                                                                  MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
   RegisterStrongMemoryReporter(new MessageManagerReporter());
   mm.forget(aResult);
   return NS_OK;
 }
 
 nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
   nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
-nsScriptCacheCleaner* nsMessageManagerScriptExecutor::sScriptCacheCleaner = nullptr;
+StaticRefPtr<nsScriptCacheCleaner> nsMessageManagerScriptExecutor::sScriptCacheCleaner;
 
 void
 nsMessageManagerScriptExecutor::DidCreateGlobal()
 {
   NS_ASSERTION(mGlobal, "Should have mGlobal!");
   if (!sCachedScripts) {
     sCachedScripts =
       new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
-
-    RefPtr<nsScriptCacheCleaner> scriptCacheCleaner =
-      new nsScriptCacheCleaner();
-    scriptCacheCleaner.forget(&sScriptCacheCleaner);
+    sScriptCacheCleaner = new nsScriptCacheCleaner();
   }
 }
 
 // static
 void
 nsMessageManagerScriptExecutor::PurgeCache()
 {
   if (sCachedScripts) {
@@ -1696,19 +1693,17 @@ nsMessageManagerScriptExecutor::PurgeCac
 void
 nsMessageManagerScriptExecutor::Shutdown()
 {
   if (sCachedScripts) {
     PurgeCache();
 
     delete sCachedScripts;
     sCachedScripts = nullptr;
-
-    RefPtr<nsScriptCacheCleaner> scriptCacheCleaner;
-    scriptCacheCleaner.swap(sScriptCacheCleaner);
+    sScriptCacheCleaner = nullptr;
   }
 }
 
 void
 nsMessageManagerScriptExecutor::LoadScriptInternal(const nsAString& aURL,
                                                    bool aRunInGlobalScope)
 {
   if (!mGlobal || !sCachedScripts) {
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -16,16 +16,17 @@
 #include "nsIAtom.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsDataHashtable.h"
 #include "nsClassHashtable.h"
 #include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "js/RootingAPI.h"
 #include "nsTObserverArray.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
@@ -401,17 +402,17 @@ protected:
                                     bool aRunInGlobalScope);
   bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
   void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
 
   static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts;
-  static nsScriptCacheCleaner* sScriptCacheCleaner;
+  static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner;
 };
 
 class nsScriptCacheCleaner final : public nsIObserver
 {
   ~nsScriptCacheCleaner() {}
 
   NS_DECL_ISUPPORTS
 
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -20,26 +20,56 @@
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPrincipal.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 
+using mozilla::DOMMediaStream;
 using mozilla::dom::BlobImpl;
+using mozilla::dom::MediaSource;
 using mozilla::ErrorResult;
 using mozilla::net::LoadInfo;
 
 // -----------------------------------------------------------------------
 // Hash table
 struct DataInfo
 {
-  // mObject is expected to be an BlobImpl, DOMMediaStream, or MediaSource
-  nsCOMPtr<nsISupports> mObject;
+  enum ObjectType {
+    eBlobImpl,
+    eMediaStream,
+    eMediaSource
+  };
+
+  DataInfo(BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal)
+    : mObjectType(eBlobImpl)
+    , mBlobImpl(aBlobImpl)
+    , mPrincipal(aPrincipal)
+  {}
+
+  DataInfo(DOMMediaStream* aMediaStream, nsIPrincipal* aPrincipal)
+    : mObjectType(eMediaStream)
+    , mMediaStream(aMediaStream)
+    , mPrincipal(aPrincipal)
+  {}
+
+  DataInfo(MediaSource* aMediaSource, nsIPrincipal* aPrincipal)
+    : mObjectType(eMediaSource)
+    , mMediaSource(aMediaSource)
+    , mPrincipal(aPrincipal)
+  {}
+
+  ObjectType mObjectType;
+
+  RefPtr<BlobImpl> mBlobImpl;
+  RefPtr<DOMMediaStream> mMediaStream;
+  RefPtr<MediaSource> mMediaSource;
+
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mStack;
 };
 
 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
 
 static DataInfo*
 GetDataInfo(const nsACString& aUri)
@@ -66,16 +96,31 @@ GetDataInfo(const nsACString& aUri)
   if (pos < 0) {
     gDataTable->Get(aUri, &res);
   } else {
     gDataTable->Get(StringHead(aUri, pos), &res);
   }
 
   return res;
 }
+static DataInfo*
+GetDataInfoFromURI(nsIURI* aURI)
+{
+  if (!aURI) {
+    return nullptr;
+  }
+
+  nsCString spec;
+  nsresult rv = aURI->GetSpec(spec);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  return GetDataInfo(spec);
+}
 
 // Memory reporting for the hash table.
 namespace mozilla {
 
 void
 BroadcastBlobURLRegistration(const nsACString& aURI,
                              BlobImpl* aBlobImpl,
                              nsIPrincipal* aPrincipal)
@@ -145,28 +190,34 @@ class BlobURLsReporter final : public ns
     if (!gDataTable) {
       return NS_OK;
     }
 
     nsDataHashtable<nsPtrHashKey<BlobImpl>, uint32_t> refCounts;
 
     // Determine number of URLs per BlobImpl, to handle the case where it's > 1.
     for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
-      nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(iter.UserData()->mObject);
-      if (blobImpl) {
-        refCounts.Put(blobImpl, refCounts.Get(blobImpl) + 1);
+      if (iter.UserData()->mObjectType != DataInfo::eBlobImpl) {
+        continue;
       }
+
+      BlobImpl* blobImpl = iter.UserData()->mBlobImpl;
+      MOZ_ASSERT(blobImpl);
+
+      refCounts.Put(blobImpl, refCounts.Get(blobImpl) + 1);
     }
 
     for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
       nsCStringHashKey::KeyType key = iter.Key();
       DataInfo* info = iter.UserData();
 
-      nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(iter.UserData()->mObject);
-      if (blobImpl) {
+      if (iter.UserData()->mObjectType == DataInfo::eBlobImpl) {
+        BlobImpl* blobImpl = iter.UserData()->mBlobImpl;
+        MOZ_ASSERT(blobImpl);
+
         NS_NAMED_LITERAL_CSTRING(desc,
           "A blob URL allocated with URL.createObjectURL; the referenced "
           "blob cannot be freed until all URLs for it have been explicitly "
           "invalidated with URL.revokeObjectURL.");
         nsAutoCString path, url, owner, specialDesc;
         nsCOMPtr<nsIURI> principalURI;
         uint64_t size = 0;
         uint32_t refCount = 1;
@@ -229,37 +280,32 @@ class BlobURLsReporter final : public ns
           aCallback->Callback(EmptyCString(),
               path,
               KIND_OTHER,
               UNITS_COUNT,
               1,
               descString,
               aData);
         }
-      } else {
-        // Just report the path for the DOMMediaStream or MediaSource.
-        nsCOMPtr<mozilla::dom::MediaSource>
-          ms(do_QueryInterface(info->mObject));
-        nsAutoCString path;
-        path = ms ? "media-source-urls/" : "dom-media-stream-urls/";
-        BuildPath(path, key, info, aAnonymize);
+        continue;
+      }
 
-        NS_NAMED_LITERAL_CSTRING(desc,
-          "An object URL allocated with URL.createObjectURL; the referenced "
-          "data cannot be freed until all URLs for it have been explicitly "
-          "invalidated with URL.revokeObjectURL.");
+      // Just report the path for the DOMMediaStream or MediaSource.
+      nsAutoCString path;
+      path = iter.UserData()->mObjectType == DataInfo::eMediaSource
+               ? "media-source-urls/" : "dom-media-stream-urls/";
+      BuildPath(path, key, info, aAnonymize);
 
-        aCallback->Callback(EmptyCString(),
-            path,
-            KIND_OTHER,
-            UNITS_COUNT,
-            1,
-            desc,
-            aData);
-      }
+      NS_NAMED_LITERAL_CSTRING(desc,
+        "An object URL allocated with URL.createObjectURL; the referenced "
+        "data cannot be freed until all URLs for it have been explicitly "
+        "invalidated with URL.revokeObjectURL.");
+
+      aCallback->Callback(EmptyCString(), path, KIND_OTHER, UNITS_COUNT, 1,
+                          desc, aData);
     }
 
     return NS_OK;
   }
 
   // Initialize info->mStack to record JS stack info, if enabled.
   // The string generated here is used in ReportCallback, below.
   static void GetJSStackForBlob(DataInfo* aInfo)
@@ -365,16 +411,32 @@ class BlobURLsReporter final : public ns
     }
   }
 };
 
 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
 
 } // namespace mozilla
 
+template<typename T>
+static nsresult
+AddDataEntryInternal(const nsACString& aURI, T aObject,
+                     nsIPrincipal* aPrincipal)
+{
+  if (!gDataTable) {
+    gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
+  }
+
+  DataInfo* info = new DataInfo(aObject, aPrincipal);
+  mozilla::BlobURLsReporter::GetJSStackForBlob(info);
+
+  gDataTable->Put(aURI, info);
+  return NS_OK;
+}
+
 void
 nsHostObjectProtocolHandler::Init(void)
 {
   static bool initialized = false;
 
   if (!initialized) {
     initialized = true;
     RegisterStrongMemoryReporter(new mozilla::HostObjectURLsReporter());
@@ -382,88 +444,93 @@ nsHostObjectProtocolHandler::Init(void)
   }
 }
 
 nsHostObjectProtocolHandler::nsHostObjectProtocolHandler()
 {
   Init();
 }
 
-nsresult
-nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aScheme,
-                                          nsISupports* aObject,
+/* static */ nsresult
+nsHostObjectProtocolHandler::AddDataEntry(BlobImpl* aBlobImpl,
+                                          nsIPrincipal* aPrincipal,
+                                          nsACString& aUri)
+{
+  Init();
+
+  nsresult rv = GenerateURIStringForBlobURL(aPrincipal, aUri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = AddDataEntryInternal(aUri, aBlobImpl, aPrincipal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  BroadcastBlobURLRegistration(aUri, aBlobImpl, aPrincipal);
+  return NS_OK;
+}
+
+/* static */ nsresult
+nsHostObjectProtocolHandler::AddDataEntry(DOMMediaStream* aMediaStream,
                                           nsIPrincipal* aPrincipal,
                                           nsACString& aUri)
 {
-#ifdef DEBUG
-  {
-    nsCOMPtr<BlobImpl> blobImpl(do_QueryInterface(aObject));
-    nsCOMPtr<MediaSource> mediaSource(do_QueryInterface(aObject));
-    nsCOMPtr<DOMMediaStream> mediaStream(do_QueryInterface(aObject));
-
-    // We support only these types.
-    MOZ_ASSERT(blobImpl || mediaSource || mediaStream);
-  }
-#endif
-
   Init();
 
-  nsresult rv = GenerateURIString(aScheme, aPrincipal, aUri);
+  nsresult rv = GenerateURIStringForBlobURL(aPrincipal, aUri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = AddDataEntryInternal(aUri, aMediaStream, aPrincipal);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = AddDataEntry(aUri, aObject, aPrincipal);
+  return NS_OK;
+}
+
+/* static */ nsresult
+nsHostObjectProtocolHandler::AddDataEntry(MediaSource* aMediaSource,
+                                          nsIPrincipal* aPrincipal,
+                                          nsACString& aUri)
+{
+  Init();
+
+  nsresult rv = GenerateURIStringForBlobURL(aPrincipal, aUri);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(aObject);
-  if (blobImpl) {
-    BroadcastBlobURLRegistration(aUri, blobImpl, aPrincipal);
-  }
+  rv = AddDataEntryInternal(aUri, aMediaSource, aPrincipal);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 /* static */ nsresult
 nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aURI,
-                                          nsISupports* aObject,
-                                          nsIPrincipal* aPrincipal)
+                                          nsIPrincipal* aPrincipal,
+                                          mozilla::dom::BlobImpl* aBlobImpl)
 {
-  if (!gDataTable) {
-    gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
-  }
-
-  DataInfo* info = new DataInfo;
-
-  info->mObject = aObject;
-  info->mPrincipal = aPrincipal;
-  mozilla::BlobURLsReporter::GetJSStackForBlob(info);
-
-  gDataTable->Put(aURI, info);
-  return NS_OK;
+  return AddDataEntryInternal(aURI, aBlobImpl, aPrincipal);
 }
 
 /* static */ bool
 nsHostObjectProtocolHandler::GetAllBlobURLEntries(nsTArray<BlobURLRegistrationData>& aRegistrations,
                                                   ContentParent* aCP)
 {
   MOZ_ASSERT(aCP);
 
   if (!gDataTable) {
     return true;
   }
 
   for (auto iter = gDataTable->ConstIter(); !iter.Done(); iter.Next()) {
     DataInfo* info = iter.UserData();
     MOZ_ASSERT(info);
 
-    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
-    if (!blobImpl) {
+    if (info->mObjectType != DataInfo::eBlobImpl) {
       continue;
     }
 
-    PBlobParent* blobParent = aCP->GetOrCreateActorForBlobImpl(blobImpl);
+    MOZ_ASSERT(info->mBlobImpl);
+    PBlobParent* blobParent = aCP->GetOrCreateActorForBlobImpl(info->mBlobImpl);
     if (!blobParent) {
       return false;
     }
 
     aRegistrations.AppendElement(
       BlobURLRegistrationData(nsCString(iter.Key()), blobParent, nullptr,
                               IPC::Principal(info->mPrincipal)));
   }
@@ -479,21 +546,18 @@ nsHostObjectProtocolHandler::RemoveDataE
     return;
   }
 
   DataInfo* info = GetDataInfo(aUri);
   if (!info) {
     return;
   }
 
-  if (aBroadcastToOtherProcesses) {
-    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
-    if (blobImpl) {
-      BroadcastBlobURLUnregistration(aUri, info);
-    }
+  if (aBroadcastToOtherProcesses && info->mObjectType == DataInfo::eBlobImpl) {
+    BroadcastBlobURLUnregistration(aUri, info);
   }
 
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
@@ -543,16 +607,24 @@ nsHostObjectProtocolHandler::GenerateURI
     aUri.Append('/');
   }
 
   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
 
   return NS_OK;
 }
 
+nsresult
+nsHostObjectProtocolHandler::GenerateURIStringForBlobURL(nsIPrincipal* aPrincipal,
+                                                         nsACString& aUri)
+{
+  return
+    GenerateURIString(NS_LITERAL_CSTRING(BLOBURI_SCHEME), aPrincipal, aUri);
+}
+
 nsIPrincipal*
 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
 {
   if (!gDataTable) {
     return nullptr;
   }
 
   DataInfo* res = GetDataInfo(aUri);
@@ -573,33 +645,24 @@ nsHostObjectProtocolHandler::Traverse(co
   }
 
   DataInfo* res;
   gDataTable->Get(aUri, &res);
   if (!res) {
     return;
   }
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "HostObjectProtocolHandler DataInfo.mObject");
-  aCallback.NoteXPCOMChild(res->mObject);
-}
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "HostObjectProtocolHandler DataInfo.mBlobImpl");
+  aCallback.NoteXPCOMChild(res->mBlobImpl);
 
-static nsISupports*
-GetDataObjectForSpec(const nsACString& aSpec)
-{
-  DataInfo* info = GetDataInfo(aSpec);
-  return info ? info->mObject : nullptr;
-}
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "HostObjectProtocolHandler DataInfo.mMediaSource");
+  aCallback.NoteXPCOMChild(res->mMediaSource);
 
-static nsISupports*
-GetDataObject(nsIURI* aURI)
-{
-  nsCString spec;
-  aURI->GetSpec(spec);
-  return GetDataObjectForSpec(spec);
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "HostObjectProtocolHandler DataInfo.mMediaStream");
+  aCallback.NoteXPCOMChild(res->mMediaStream);
 }
 
 // -----------------------------------------------------------------------
 // Protocol handler
 
 NS_IMPL_ISUPPORTS(nsHostObjectProtocolHandler, nsIProtocolHandler)
 
 NS_IMETHODIMP
@@ -624,19 +687,19 @@ nsHostObjectProtocolHandler::NewURI(cons
                                     nsIURI **aResult)
 {
   *aResult = nullptr;
   nsresult rv;
 
   DataInfo* info = GetDataInfo(aSpec);
 
   RefPtr<nsHostObjectURI> uri;
-  if (info) {
-    nsCOMPtr<BlobImpl> blob = do_QueryInterface(info->mObject);
-    uri = new nsHostObjectURI(info->mPrincipal, blob);
+  if (info && info->mObjectType == DataInfo::eBlobImpl) {
+    MOZ_ASSERT(info->mBlobImpl);
+    uri = new nsHostObjectURI(info->mPrincipal, info->mBlobImpl);
   } else {
     uri = new nsHostObjectURI(nullptr, nullptr);
   }
 
   rv = uri->SetSpec(aSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_TryToSetImmutable(uri);
@@ -660,20 +723,17 @@ nsHostObjectProtocolHandler::NewChannel2
   nsCOMPtr<nsISupports> tmp;
   MOZ_ALWAYS_SUCCEEDS(uriBlobImpl->GetBlobImpl(getter_AddRefs(tmp)));
   nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(tmp);
   if (!blobImpl) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
 #ifdef DEBUG
-  nsCString spec;
-  uri->GetSpec(spec);
-
-  DataInfo* info = GetDataInfo(spec);
+  DataInfo* info = GetDataInfoFromURI(uri);
 
   // Info can be null, in case this blob URL has been revoked already.
   if (info) {
     nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
     nsCOMPtr<nsIPrincipal> principal;
     uriPrinc->GetPrincipal(getter_AddRefs(principal));
     NS_ASSERTION(info->mPrincipal == principal, "Wrong principal!");
   }
@@ -738,62 +798,50 @@ nsHostObjectProtocolHandler::AllowPort(i
 NS_IMETHODIMP
 nsBlobProtocolHandler::GetScheme(nsACString &result)
 {
   result.AssignLiteral(BLOBURI_SCHEME);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsMediaStreamProtocolHandler::GetScheme(nsACString &result)
-{
-  result.AssignLiteral(MEDIASTREAMURI_SCHEME);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsMediaSourceProtocolHandler::GetScheme(nsACString &result)
-{
-  result.AssignLiteral(MEDIASOURCEURI_SCHEME);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsFontTableProtocolHandler::GetScheme(nsACString &result)
 {
   result.AssignLiteral(FONTTABLEURI_SCHEME);
   return NS_OK;
 }
 
 nsresult
 NS_GetBlobForBlobURI(nsIURI* aURI, BlobImpl** aBlob)
 {
   NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
 
   *aBlob = nullptr;
 
-  nsCOMPtr<BlobImpl> blob = do_QueryInterface(GetDataObject(aURI));
-  if (!blob) {
+  DataInfo* info = GetDataInfoFromURI(aURI);
+  if (!info || info->mObjectType != DataInfo::eBlobImpl) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
+  RefPtr<BlobImpl> blob = info->mBlobImpl;
   blob.forget(aBlob);
   return NS_OK;
 }
 
 nsresult
 NS_GetBlobForBlobURISpec(const nsACString& aSpec, BlobImpl** aBlob)
 {
   *aBlob = nullptr;
 
-  nsCOMPtr<BlobImpl> blob = do_QueryInterface(GetDataObjectForSpec(aSpec));
-  if (!blob) {
+  DataInfo* info = GetDataInfo(aSpec);
+  if (!info || info->mObjectType != DataInfo::eBlobImpl) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
+  RefPtr<BlobImpl> blob = info->mBlobImpl;
   blob.forget(aBlob);
   return NS_OK;
 }
 
 nsresult
 NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
 {
   RefPtr<BlobImpl> blobImpl;
@@ -811,23 +859,24 @@ NS_GetStreamForBlobURI(nsIURI* aURI, nsI
   return NS_OK;
 }
 
 nsresult
 NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream)
 {
   NS_ASSERTION(IsMediaStreamURI(aURI), "Only call this with mediastream URIs");
 
-  nsISupports* dataObject = GetDataObject(aURI);
-  if (!dataObject) {
+  DataInfo* info = GetDataInfoFromURI(aURI);
+  if (!info || info->mObjectType != DataInfo::eMediaStream) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
-  *aStream = nullptr;
-  return CallQueryInterface(dataObject, aStream);
+  RefPtr<DOMMediaStream> mediaStream = info->mMediaStream;
+  mediaStream.forget(aStream);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFontTableProtocolHandler::NewURI(const nsACString& aSpec,
                                    const char *aCharset,
                                    nsIURI *aBaseURI,
                                    nsIURI **aResult)
 {
@@ -860,67 +909,76 @@ nsFontTableProtocolHandler::NewURI(const
 
 nsresult
 NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource)
 {
   NS_ASSERTION(IsMediaSourceURI(aURI), "Only call this with mediasource URIs");
 
   *aSource = nullptr;
 
-  nsCOMPtr<mozilla::dom::MediaSource> source = do_QueryInterface(GetDataObject(aURI));
-  if (!source) {
+  DataInfo* info = GetDataInfoFromURI(aURI);
+  if (!info || info->mObjectType != DataInfo::eMediaSource) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
-  source.forget(aSource);
+  RefPtr<MediaSource> mediaSource = info->mMediaSource;
+  mediaSource.forget(aSource);
   return NS_OK;
 }
 
 #define NS_BLOBPROTOCOLHANDLER_CID \
 { 0xb43964aa, 0xa078, 0x44b2, \
   { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
 
-#define NS_MEDIASTREAMPROTOCOLHANDLER_CID \
-{ 0x27d1fa24, 0x2b73, 0x4db3, \
-  { 0xab, 0x48, 0xb9, 0x83, 0x83, 0x40, 0xe0, 0x81 } }
-
-#define NS_MEDIASOURCEPROTOCOLHANDLER_CID \
-{ 0x12ef31fc, 0xa8fb, 0x4661, \
-  { 0x9a, 0x63, 0xfb, 0x61, 0x04,0x5d, 0xb8, 0x61 } }
-
 #define NS_FONTTABLEPROTOCOLHANDLER_CID \
 { 0x3fc8f04e, 0xd719, 0x43ca, \
   { 0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 } }
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSourceProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontTableProtocolHandler)
 
 NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID);
-NS_DEFINE_NAMED_CID(NS_MEDIASTREAMPROTOCOLHANDLER_CID);
-NS_DEFINE_NAMED_CID(NS_MEDIASOURCEPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOLHANDLER_CID);
 
 static const mozilla::Module::CIDEntry kHostObjectProtocolHandlerCIDs[] = {
   { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, nsBlobProtocolHandlerConstructor },
-  { &kNS_MEDIASTREAMPROTOCOLHANDLER_CID, false, nullptr, nsMediaStreamProtocolHandlerConstructor },
-  { &kNS_MEDIASOURCEPROTOCOLHANDLER_CID, false, nullptr, nsMediaSourceProtocolHandlerConstructor },
   { &kNS_FONTTABLEPROTOCOLHANDLER_CID, false, nullptr, nsFontTableProtocolHandlerConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kHostObjectProtocolHandlerContracts[] = {
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID },
-  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASTREAMURI_SCHEME, &kNS_MEDIASTREAMPROTOCOLHANDLER_CID },
-  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASOURCEURI_SCHEME, &kNS_MEDIASOURCEPROTOCOLHANDLER_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FONTTABLEURI_SCHEME, &kNS_FONTTABLEPROTOCOLHANDLER_CID },
   { nullptr }
 };
 
 static const mozilla::Module kHostObjectProtocolHandlerModule = {
   mozilla::Module::kVersion,
   kHostObjectProtocolHandlerCIDs,
   kHostObjectProtocolHandlerContracts
 };
 
 NSMODULE_DEFN(HostObjectProtocolHandler) = &kHostObjectProtocolHandlerModule;
 
+bool IsType(nsIURI* aUri, DataInfo::ObjectType aType)
+{
+  DataInfo* info = GetDataInfoFromURI(aUri);
+  if (!info) {
+    return false;
+  }
+
+  return info->mObjectType == aType;
+}
+
+bool IsBlobURI(nsIURI* aUri)
+{
+  return IsType(aUri, DataInfo::eBlobImpl);
+}
+
+bool IsMediaStreamURI(nsIURI* aUri)
+{
+  return IsType(aUri, DataInfo::eMediaStream);
+}
+
+bool IsMediaSourceURI(nsIURI* aUri)
+{
+  return IsType(aUri, DataInfo::eMediaSource);
+}
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -10,24 +10,23 @@
 #include "mozilla/Attributes.h"
 #include "nsIProtocolHandler.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
 #include "nsTArray.h"
 
 #define BLOBURI_SCHEME "blob"
-#define MEDIASTREAMURI_SCHEME "mediastream"
-#define MEDIASOURCEURI_SCHEME "mediasource"
 #define FONTTABLEURI_SCHEME "moz-fonttable"
 #define RTSPURI_SCHEME "rtsp"
 
 class nsIPrincipal;
 
 namespace mozilla {
+class BlobURLsReporter;
 class DOMMediaStream;
 namespace dom {
 class BlobImpl;
 class BlobURLRegistrationData;
 class ContentParent;
 class MediaSource;
 } // namespace dom
 } // namespace mozilla
@@ -46,36 +45,44 @@ public:
   NS_IMETHOD NewChannel2(nsIURI *aURI, nsILoadInfo *aLoadinfo, nsIChannel * *_retval) override;
   NS_IMETHOD NewChannel(nsIURI *aURI, nsIChannel * *_retval) override;
   NS_IMETHOD AllowPort(int32_t port, const char * scheme, bool *_retval) override;
 
   // If principal is not null, its origin will be used to generate the URI.
   static nsresult GenerateURIString(const nsACString &aScheme,
                                     nsIPrincipal* aPrincipal,
                                     nsACString &aUri);
+  static nsresult GenerateURIStringForBlobURL(nsIPrincipal* aPrincipal,
+                                              nsACString &aUri);
 
   // Methods for managing uri->object mapping
   // AddDataEntry creates the URI with the given scheme and returns it in aUri
-  static nsresult AddDataEntry(const nsACString& aScheme,
-                               nsISupports* aObject,
+  static nsresult AddDataEntry(mozilla::dom::BlobImpl* aBlobImpl,
+                               nsIPrincipal* aPrincipal,
+                               nsACString& aUri);
+  static nsresult AddDataEntry(mozilla::DOMMediaStream* aMediaStream,
                                nsIPrincipal* aPrincipal,
                                nsACString& aUri);
+  static nsresult AddDataEntry(mozilla::dom::MediaSource* aMediaSource,
+                               nsIPrincipal* aPrincipal,
+                               nsACString& aUri);
+  // IPC only
+  static nsresult AddDataEntry(const nsACString& aURI,
+                               nsIPrincipal* aPrincipal,
+                               mozilla::dom::BlobImpl* aBlobImpl);
+
   static void RemoveDataEntry(const nsACString& aUri,
                               bool aBroadcastToOTherProcesses = true);
 
   // This is for IPC only.
   static void RemoveDataEntries();
 
   static nsIPrincipal* GetDataEntryPrincipal(const nsACString& aUri);
   static void Traverse(const nsACString& aUri, nsCycleCollectionTraversalCallback& aCallback);
 
-  // IPC or internal use only
-  static nsresult AddDataEntry(const nsACString& aURI,
-                               nsISupports* aObject,
-                               nsIPrincipal* aPrincipal);
   static bool
   GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
                        mozilla::dom::ContentParent* aCP);
 
 protected:
   virtual ~nsHostObjectProtocolHandler() {}
 
 private:
@@ -83,59 +90,39 @@ private:
 };
 
 class nsBlobProtocolHandler : public nsHostObjectProtocolHandler
 {
 public:
   NS_IMETHOD GetScheme(nsACString &result) override;
 };
 
-class nsMediaStreamProtocolHandler : public nsHostObjectProtocolHandler
-{
-public:
-  NS_IMETHOD GetScheme(nsACString &result) override;
-};
-
 class nsMediaSourceProtocolHandler : public nsHostObjectProtocolHandler
 {
 public:
   NS_IMETHOD GetScheme(nsACString &result) override;
 };
 
 class nsFontTableProtocolHandler : public nsHostObjectProtocolHandler
 {
 public:
   NS_IMETHOD GetScheme(nsACString &result);
   NS_IMETHOD NewURI(const nsACString & aSpec, const char * aOriginCharset, nsIURI *aBaseURI, nsIURI * *_retval);
 };
 
-inline bool IsBlobURI(nsIURI* aUri)
-{
-  bool isBlob;
-  return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
-}
+bool IsBlobURI(nsIURI* aUri);
+bool IsMediaStreamURI(nsIURI* aUri);
+bool IsMediaSourceURI(nsIURI* aUri);
 
 inline bool IsRtspURI(nsIURI* aUri)
 {
   bool isRtsp;
   return NS_SUCCEEDED(aUri->SchemeIs(RTSPURI_SCHEME, &isRtsp)) && isRtsp;
 }
 
-inline bool IsMediaStreamURI(nsIURI* aUri)
-{
-  bool isStream;
-  return NS_SUCCEEDED(aUri->SchemeIs(MEDIASTREAMURI_SCHEME, &isStream)) && isStream;
-}
-
-inline bool IsMediaSourceURI(nsIURI* aUri)
-{
-  bool isMediaSource;
-  return NS_SUCCEEDED(aUri->SchemeIs(MEDIASOURCEURI_SCHEME, &isMediaSource)) && isMediaSource;
-}
-
 inline bool IsFontTableURI(nsIURI* aUri)
 {
   bool isFont;
   return NS_SUCCEEDED(aUri->SchemeIs(FONTTABLEURI_SCHEME, &isFont)) && isFont;
 }
 
 extern nsresult
 NS_GetBlobForBlobURI(nsIURI* aURI, mozilla::dom::BlobImpl** aBlob);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -136,17 +136,16 @@ static const uint32_t kMaxICCDuration = 
 #define NS_CC_PURPLE_LIMIT          200
 
 // Large value used to specify that a script should run essentially forever
 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
 
 // if you add statics here, add them to the list in StartupJSEnvironment
 
 static nsITimer *sGCTimer;
-static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sShrinkingGCTimer;
 static nsITimer *sCCTimer;
 static nsITimer *sICCTimer;
 static nsITimer *sFullGCTimer;
 static nsITimer *sInterSliceGCTimer;
 
 static TimeStamp sLastCCEndTime;
 
@@ -290,17 +289,16 @@ GetCollectionTimeDelta()
   return 0;
 }
 
 static void
 KillTimers()
 {
   nsJSContext::KillGCTimer();
   nsJSContext::KillShrinkingGCTimer();
-  nsJSContext::KillShrinkGCBuffersTimer();
   nsJSContext::KillCCTimer();
   nsJSContext::KillICCTimer();
   nsJSContext::KillFullGCTimer();
   nsJSContext::KillInterSliceGCTimer();
 }
 
 // If we collected a substantial amount of cycles, poke the GC since more objects
 // might be unreachable now.
@@ -1183,17 +1181,16 @@ nsJSContext::GarbageCollectNow(JS::gcrea
                                int64_t aSliceMillis)
 {
   PROFILER_LABEL("nsJSContext", "GarbageCollectNow",
     js::ProfileEntry::Category::GC);
 
   MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
 
   KillGCTimer();
-  KillShrinkGCBuffersTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
@@ -1221,28 +1218,16 @@ nsJSContext::GarbageCollectNow(JS::gcrea
 
   if (aIncremental == IncrementalGC) {
     JS::StartIncrementalGC(sContext, gckind, aReason, aSliceMillis);
   } else {
     JS::GCForReason(sContext, gckind, aReason);
   }
 }
 
-//static
-void
-nsJSContext::ShrinkGCBuffersNow()
-{
-  PROFILER_LABEL("nsJSContext", "ShrinkGCBuffersNow",
-    js::ProfileEntry::Category::GC);
-
-  KillShrinkGCBuffersTimer();
-
-  JS::ShrinkGCBuffers(sContext);
-}
-
 static void
 FinishAnyIncrementalGC()
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GC);
 
   if (sCCLockedOut) {
     // We're in the middle of an incremental GC, so finish it.
     JS::PrepareForIncrementalGC(sContext);
@@ -1726,23 +1711,16 @@ void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillGCTimer();
   uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
   nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
                                  nsJSContext::IncrementalGC);
 }
 
-void
-ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
-{
-  nsJSContext::KillShrinkGCBuffersTimer();
-  nsJSContext::ShrinkGCBuffersNow();
-}
-
 // static
 void
 ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
 {
   nsJSContext::KillShrinkingGCTimer();
   sIsCompactingOnUserInactive = true;
   nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
                                  nsJSContext::IncrementalGC,
@@ -1961,38 +1939,16 @@ nsJSContext::PokeGC(JS::gcreason::Reason
                                          : NS_GC_DELAY),
                                       nsITimer::TYPE_ONE_SHOT,
                                       "GCTimerFired");
   first = false;
 }
 
 // static
 void
-nsJSContext::PokeShrinkGCBuffers()
-{
-  if (sShrinkGCBuffersTimer || sShuttingDown) {
-    return;
-  }
-
-  CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
-
-  if (!sShrinkGCBuffersTimer) {
-    // Failed to create timer (probably because we're in XPCOM shutdown)
-    return;
-  }
-
-  sShrinkGCBuffersTimer->InitWithNamedFuncCallback(ShrinkGCBuffersTimerFired,
-                                                   nullptr,
-                                                   NS_SHRINK_GC_BUFFERS_DELAY,
-                                                   nsITimer::TYPE_ONE_SHOT,
-                                                   "ShrinkGCBuffersTimerFired");
-}
-
-// static
-void
 nsJSContext::PokeShrinkingGC()
 {
   if (sShrinkingGCTimer || sShuttingDown) {
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sShrinkingGCTimer);
 
@@ -2056,26 +2012,16 @@ nsJSContext::KillInterSliceGCTimer()
   if (sInterSliceGCTimer) {
     sInterSliceGCTimer->Cancel();
     NS_RELEASE(sInterSliceGCTimer);
   }
 }
 
 //static
 void
-nsJSContext::KillShrinkGCBuffersTimer()
-{
-  if (sShrinkGCBuffersTimer) {
-    sShrinkGCBuffersTimer->Cancel();
-    NS_RELEASE(sShrinkGCBuffersTimer);
-  }
-}
-
-//static
-void
 nsJSContext::KillShrinkingGCTimer()
 {
   if (sShrinkingGCTimer) {
     sShrinkingGCTimer->Cancel();
     NS_RELEASE(sShrinkingGCTimer);
   }
 }
 
@@ -2133,19 +2079,16 @@ static void
 DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
 {
   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
 
   switch (aProgress) {
     case JS::GC_CYCLE_BEGIN: {
       // Prevent cycle collections and shrinking during incremental GC.
       sCCLockedOut = true;
-
-      nsJSContext::KillShrinkGCBuffersTimer();
-
       break;
     }
 
     case JS::GC_CYCLE_END: {
       PRTime delta = GetCollectionTimeDelta();
 
       if (sPostGCEventsToConsole) {
         NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f)[%s] ");
@@ -2190,20 +2133,16 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
                                                   NS_FULL_GC_DELAY,
                                                   nsITimer::TYPE_ONE_SHOT,
                                                   "FullGCTimerFired");
         }
       } else {
         nsJSContext::KillFullGCTimer();
       }
 
-      if (aDesc.invocationKind_ == GC_NORMAL) {
-        nsJSContext::PokeShrinkGCBuffers();
-      }
-
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       break;
     }
 
     case JS::GC_SLICE_BEGIN:
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -82,17 +82,16 @@ public:
 
   // Setup all the statics etc - safe to call multiple times after Startup().
   void EnsureStatics();
 
   static void GarbageCollectNow(JS::gcreason::Reason reason,
                                 IsIncremental aIncremental = NonIncrementalGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
-  static void ShrinkGCBuffersNow();
 
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr,
                               int32_t aExtraForgetSkippableCalls = 0);
 
   // Run a cycle collector slice, using a heuristic to decide how long to run it.
   static void RunCycleCollectorSlice();
@@ -107,19 +106,16 @@ public:
   static uint32_t GetMaxCCSliceTimeSinceClear();
   static void ClearMaxCCSliceTime();
 
   static void RunNextCollectorTimer();
 
   static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
   static void KillGCTimer();
 
-  static void PokeShrinkGCBuffers();
-  static void KillShrinkGCBuffersTimer();
-
   static void PokeShrinkingGC();
   static void KillShrinkingGCTimer();
 
   static void MaybePokeCC();
   static void KillCCTimer();
   static void KillICCTimer();
   static void KillFullGCTimer();
   static void KillInterSliceGCTimer();
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -198,17 +198,26 @@ GetCurrentJSStack(int32_t aMaxDepth)
 {
   // is there a current context available?
   JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
 
   if (!cx || !js::GetContextCompartment(cx)) {
     return nullptr;
   }
 
-  return dom::exceptions::CreateStack(cx, aMaxDepth);
+  static const unsigned MAX_FRAMES = 100;
+  if (aMaxDepth < 0) {
+    aMaxDepth = MAX_FRAMES;
+  }
+
+  JS::StackCapture captureMode = aMaxDepth == 0
+    ? JS::StackCapture(JS::AllFrames())
+    : JS::StackCapture(JS::MaxFrames(aMaxDepth));
+
+  return dom::exceptions::CreateStack(cx, mozilla::Move(captureMode));
 }
 
 namespace exceptions {
 
 class JSStackFrame : public nsIStackFrame
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -660,25 +669,20 @@ NS_IMETHODIMP JSStackFrame::ToString(JSC
   _retval.AppendPrintf(format,
                        NS_ConvertUTF16toUTF8(filename).get(),
                        NS_ConvertUTF16toUTF8(funname).get(),
                        lineno);
   return NS_OK;
 }
 
 already_AddRefed<nsIStackFrame>
-CreateStack(JSContext* aCx, int32_t aMaxDepth)
+CreateStack(JSContext* aCx, JS::StackCapture&& aCaptureMode)
 {
-  static const unsigned MAX_FRAMES = 100;
-  if (aMaxDepth < 0) {
-    aMaxDepth = MAX_FRAMES;
-  }
-
   JS::Rooted<JSObject*> stack(aCx);
-  if (!JS::CaptureCurrentStack(aCx, &stack, aMaxDepth)) {
+  if (!JS::CaptureCurrentStack(aCx, &stack, mozilla::Move(aCaptureMode))) {
     return nullptr;
   }
 
   if (!stack) {
     return nullptr;
   }
 
   nsCOMPtr<nsIStackFrame> frame = new JSStackFrame(stack);
--- a/dom/bindings/Exceptions.h
+++ b/dom/bindings/Exceptions.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_Exceptions_h__
 
 // DOM exception throwing machinery (for both main thread and workers).
 
 #include <stdint.h>
 #include "jspubtd.h"
 #include "nsIException.h"
 #include "nsStringGlue.h"
+#include "jsapi.h"
 
 class nsIStackFrame;
 class nsPIDOMWindowInner;
 template <class T>
 struct already_AddRefed;
 
 namespace mozilla {
 namespace dom {
@@ -52,19 +53,16 @@ CreateException(JSContext* aCx, nsresult
 // value is -1, a default maximal depth will be selected.  Will return null if
 // there is no JS stack right now.
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack(int32_t aMaxDepth = -1);
 
 // Internal stuff not intended to be widely used.
 namespace exceptions {
 
-// aMaxDepth can be used to define a maximal depth for the stack trace. If the
-// value is -1, a default maximal depth will be selected.  Will return null if
-// there is no JS stack right now.
 already_AddRefed<nsIStackFrame>
-CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
+CreateStack(JSContext* aCx, JS::StackCapture&& aCaptureMode);
 
 } // namespace exceptions
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -317,17 +317,17 @@ WebGLContext::BufferSubDataT(GLenum targ
     gl->fBufferSubData(target, byteOffset, data.LengthAllowShared(), data.DataAllowShared());
 }
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                             const dom::Nullable<dom::ArrayBuffer>& maybeData)
 {
     if (maybeData.IsNull()) {
-        // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386
+        ErrorInvalidValue("BufferSubData: returnedData is null.");
         return;
     }
     BufferSubDataT(target, byteOffset, maybeData.Value());
 }
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                             const dom::SharedArrayBuffer& data)
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4624,17 +4624,16 @@ skip-if = (os == 'android' || os == 'lin
 [generated/test_2_conformance__attribs__gl-vertexattribpointer-offsets.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__buffer-bind-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__buffer-data-and-buffer-sub-data.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__buffer-data-array-buffer-delete.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__element-array-buffer-delete-recreate.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__index-validation-copies-indices.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__buffers__index-validation-crash-with-buffer-sub-data.html]
@@ -5944,17 +5943,16 @@ fail-if = (os == 'android')
 fail-if = (os == 'linux')
 [generated/test_conformance__attribs__gl-vertex-attrib-zero-issues.html]
 [generated/test_conformance__attribs__gl-vertex-attrib.html]
 [generated/test_conformance__attribs__gl-vertexattribpointer-offsets.html]
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__buffers__buffer-bind-test.html]
 [generated/test_conformance__buffers__buffer-data-and-buffer-sub-data.html]
-fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__buffers__buffer-data-array-buffer-delete.html]
 [generated/test_conformance__buffers__element-array-buffer-delete-recreate.html]
 [generated/test_conformance__buffers__index-validation-copies-indices.html]
 [generated/test_conformance__buffers__index-validation-crash-with-buffer-sub-data.html]
 [generated/test_conformance__buffers__index-validation-large-buffer.html]
 [generated/test_conformance__buffers__index-validation-verifies-too-many-indices.html]
 [generated/test_conformance__buffers__index-validation-with-resized-buffer.html]
 [generated/test_conformance__buffers__index-validation.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -148,34 +148,30 @@ fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__buffers__buffer-copying-restrictions.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__glsl3__forbidden-operators.html]
 fail-if = (os == 'mac') || (os == 'win')
-[generated/test_2_conformance__buffers__buffer-data-and-buffer-sub-data.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__rendering__negative-one-index.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance2__buffers__buffer-copying-contents.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__reading__read-pixels-pack-parameters.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
-[generated/test_conformance__buffers__buffer-data-and-buffer-sub-data.html]
-fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__misc__expando-loss-2.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
 fail-if = (os == 'android') || (os == 'linux')
 [generated/test_conformance__ogles__GL__gl_FragCoord__gl_FragCoord_001_to_003.html]
 fail-if = (os == 'android') || (os == 'linux')
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -1246,19 +1246,20 @@ Console::MethodInternal(JSContext* aCx, 
     nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
     if (NS_WARN_IF(!principal)) {
       return;
     }
 
     callData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
   }
 
-  uint32_t maxDepth = ShouldIncludeStackTrace(aMethodName) ?
-                      DEFAULT_MAX_STACKTRACE_DEPTH : 1;
-  nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
+  JS::StackCapture captureMode = ShouldIncludeStackTrace(aMethodName) ?
+    JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH)) :
+    JS::StackCapture(JS::FirstSubsumedFrame(aCx));
+  nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, mozilla::Move(captureMode));
 
   if (stack) {
     callData->mTopStackFrame.emplace();
     nsresult rv = StackFrameToStackEntry(aCx, stack,
                                          *callData->mTopStackFrame);
     if (NS_FAILED(rv)) {
       return;
     }
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -7,27 +7,30 @@
 #ifndef mozilla_EventStates_h_
 #define mozilla_EventStates_h_
 
 #include "mozilla/Attributes.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 
+#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 6
+
 /**
  * EventStates is the class used to represent the event states of nsIContent
  * instances. These states are calculated by IntrinsicState() and
  * ContentStatesChanged() has to be called when one of them changes thus
  * informing the layout/style engine of the change.
  * Event states are associated with pseudo-classes.
  */
 class EventStates
 {
 public:
   typedef uint64_t InternalType;
+  typedef uint8_t ServoType;
 
   constexpr EventStates()
     : mStates(0)
   {
   }
 
   // NOTE: the ideal scenario would be to have the default constructor public
   // setting mStates to 0 and this constructor (without = 0) private.
@@ -150,16 +153,24 @@ public:
   }
 
   // We only need that method for inDOMUtils::GetContentState.
   // If inDOMUtils::GetContentState is removed, this method should be removed.
   InternalType GetInternalValue() const {
     return mStates;
   }
 
+  /**
+   * Method used to get the appropriate state representation for Servo.
+   */
+  ServoType ServoValue() const
+  {
+    return mStates & ((1 << (NS_EVENT_STATE_HIGHEST_SERVO_BIT + 1)) - 1);
+  }
+
 private:
   InternalType mStates;
 };
 
 } // namespace mozilla
 
 /**
  * The following macros are creating EventStates instance with different
@@ -195,20 +206,21 @@ private:
 #define NS_EVENT_STATE_ENABLED       NS_DEFINE_EVENT_STATE_MACRO(3)
 // Content is disabled.
 #define NS_EVENT_STATE_DISABLED      NS_DEFINE_EVENT_STATE_MACRO(4)
 // Content is checked.
 #define NS_EVENT_STATE_CHECKED       NS_DEFINE_EVENT_STATE_MACRO(5)
 // Content is in the indeterminate state.
 #define NS_EVENT_STATE_INDETERMINATE NS_DEFINE_EVENT_STATE_MACRO(6)
 
-#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 6
-
 /*
  * Bits below here do not have Servo-related ordering constraints.
+ *
+ * Remember to change NS_EVENT_STATE_HIGHEST_SERVO_BIT at the top of the file if
+ * this changes!
  */
 
 // Drag is hovering over content.
 #define NS_EVENT_STATE_DRAGOVER      NS_DEFINE_EVENT_STATE_MACRO(7)
 // Content is URL's target (ref).
 #define NS_EVENT_STATE_URLTARGET     NS_DEFINE_EVENT_STATE_MACRO(8)
 // Content is required.
 #define NS_EVENT_STATE_REQUIRED      NS_DEFINE_EVENT_STATE_MACRO(9)
--- a/dom/gamepad/android/AndroidGamepad.cpp
+++ b/dom/gamepad/android/AndroidGamepad.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "AndroidBridge.h"
+#include "GeneratedJNIWrappers.h"
 
 namespace mozilla {
 namespace dom {
 
 void StartGamepadMonitoring()
 {
-  widget::GeckoAppShell::StartMonitoringGamepad();
+  java::GeckoAppShell::StartMonitoringGamepad();
 }
 
 void StopGamepadMonitoring()
 {
-  widget::GeckoAppShell::StopMonitoringGamepad();
+  java::GeckoAppShell::StopMonitoringGamepad();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1495,19 +1495,25 @@ nsresult HTMLMediaElement::LoadResource(
   if (docShell && !docShell->GetAllowMedia()) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the media element's CORS mode only when loading a resource
   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
 
 #ifdef MOZ_EME
+  bool isBlob = false;
   if (mMediaKeys &&
-      !IsMediaSourceURI(mLoadingSrc) &&
-      Preferences::GetBool("media.eme.mse-only", true)) {
+      Preferences::GetBool("media.eme.mse-only", true) &&
+      // We only want mediaSource URLs, but they are BlobURL, so we have to
+      // check the schema and if they are not MediaStream or real Blob.
+      (NS_FAILED(mLoadingSrc->SchemeIs(BLOBURI_SCHEME, &isBlob)) ||
+       !isBlob ||
+       IsMediaStreamURI(mLoadingSrc) ||
+       IsBlobURI(mLoadingSrc))) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 #endif
 
   HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
   if (other && other->mDecoder) {
     // Clone it.
     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2639,18 +2639,19 @@ bool
 ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations)
 {
   for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
     BlobURLRegistrationData& registration = aRegistrations[i];
     RefPtr<BlobImpl> blobImpl =
       static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl();
     MOZ_ASSERT(blobImpl);
 
-    nsHostObjectProtocolHandler::AddDataEntry(registration.url(), blobImpl,
-                                              registration.principal());
+    nsHostObjectProtocolHandler::AddDataEntry(registration.url(),
+                                              registration.principal(),
+                                              blobImpl);
   }
 
   return true;
 }
 
 bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
 {
@@ -3391,17 +3392,17 @@ ContentChild::RecvNotifyPushSubscription
 
 bool
 ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
                                       const IPC::Principal& aPrincipal)
 {
   RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl();
   MOZ_ASSERT(blobImpl);
 
-  nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl, aPrincipal);
+  nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, blobImpl);
   return true;
 }
 
 bool
 ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
 {
   nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
   return true;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2833,17 +2833,17 @@ ContentParent::RecvGetIconForExtension(c
 bool
 ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
 {
   // default behavior is to show the last password character
   *showPassword = true;
 #ifdef MOZ_WIDGET_ANDROID
   NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
 
-  *showPassword = mozilla::widget::GeckoAppShell::GetShowPasswordSetting();
+  *showPassword = java::GeckoAppShell::GetShowPasswordSetting();
 #endif
   return true;
 }
 
 bool
 ContentParent::RecvFirstIdle()
 {
   // When the ContentChild goes idle, it sends us a FirstIdle message
@@ -5744,18 +5744,18 @@ ContentParent::RecvStoreAndBroadcastBlob
                                                         const Principal& aPrincipal)
 {
   RefPtr<BlobImpl> blobImpl =
     static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
   if (NS_WARN_IF(!blobImpl)) {
     return false;
   }
 
-  if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl,
-                                                             aPrincipal))) {
+  if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal,
+                                                             blobImpl))) {
     BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
 
     // We want to store this blobURL, so we can unregister it if the child
     // crashes.
     mBlobURLs.AppendElement(aURI);
   }
 
   BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -17,17 +17,16 @@
 #include "nsContentTypeParser.h"
 #include "VideoUtils.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsIGfxInfo.h"
-#include "AndroidBridge.h"
 #endif
 #include "mozilla/layers/LayersTypes.h"
 
 #include "PDMFactory.h"
 
 namespace mozilla {
 
 MP4Decoder::MP4Decoder(MediaDecoderOwner* aOwner)
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -27,20 +27,16 @@
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "nsServiceManagerUtils.h"
 #include "gfxPlatform.h"
 #include "mozilla/Snprintf.h"
 
-#ifdef MOZ_WIDGET_ANDROID
-#include "AndroidBridge.h"
-#endif
-
 struct JSContext;
 class JSObject;
 
 mozilla::LogModule* GetMediaSourceLog()
 {
   static mozilla::LazyLogModule sLogModule("MediaSource");
   return sLogModule;
 }
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -30,17 +30,17 @@ static PRLogModuleInfo* AndroidDecoderMo
 
 #undef LOG
 #define LOG(arg, ...) MOZ_LOG(AndroidDecoderModuleLog(), \
     mozilla::LogLevel::Debug, ("AndroidDecoderModule(%p)::%s: " arg, \
       this, __func__, ##__VA_ARGS__))
 
 using namespace mozilla;
 using namespace mozilla::gl;
-using namespace mozilla::widget::sdk;
+using namespace mozilla::java::sdk;
 using media::TimeUnit;
 
 namespace mozilla {
 
 #define INVOKE_CALLBACK(Func, ...) \
   if (mCallback) { \
     mCallback->Func(__VA_ARGS__); \
   } else { \
@@ -169,36 +169,36 @@ class AudioDataDecoder : public MediaCod
 public:
   AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
                    MediaDataDecoderCallback* aCallback)
     : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
                             aFormat, aCallback)
   {
     JNIEnv* const env = jni::GetEnvForThread();
 
-    jni::Object::LocalRef buffer(env);
+    jni::ByteBuffer::LocalRef buffer(env);
     NS_ENSURE_SUCCESS_VOID(aFormat->GetByteBuffer(NS_LITERAL_STRING("csd-0"),
                                                   &buffer));
 
     if (!buffer && aConfig.mCodecSpecificConfig->Length() >= 2) {
-      buffer = jni::Object::LocalRef::Adopt(
-          env, env->NewDirectByteBuffer(aConfig.mCodecSpecificConfig->Elements(),
-          aConfig.mCodecSpecificConfig->Length()));
+      buffer = jni::ByteBuffer::New(
+          aConfig.mCodecSpecificConfig->Elements(),
+          aConfig.mCodecSpecificConfig->Length());
       NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"),
                                                     buffer));
     }
   }
 
   const char* GetDescriptionName() const override
   {
     return "android audio decoder";
   }
 
   nsresult Output(BufferInfo::Param aInfo, void* aBuffer,
-                  MediaFormat::Param aFormat, const TimeUnit& aDuration)
+                  MediaFormat::Param aFormat, const TimeUnit& aDuration) override
   {
     // The output on Android is always 16-bit signed
     nsresult rv;
     int32_t numChannels;
     NS_ENSURE_SUCCESS(rv =
         aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &numChannels), rv);
     AudioConfig::ChannelLayout layout(numChannels);
     if (!layout.IsValid()) {
@@ -272,17 +272,17 @@ AndroidDecoderModule::SupportsMimeType(c
 
   if ((VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8) &&
        !GetFeatureStatus(nsIGfxInfo::FEATURE_VP8_HW_DECODE)) ||
       (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9) &&
        !GetFeatureStatus(nsIGfxInfo::FEATURE_VP9_HW_DECODE))) {
     return false;
   }
 
-  return widget::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
+  return java::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
       nsCString(TranslateMimeType(aMimeType)));
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
   MediaFormat::LocalRef format;
 
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -38,17 +38,17 @@ public:
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 };
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
 
   MediaCodecDataDecoder(MediaData::Type aType,
                         const nsACString& aMimeType,
-                        widget::sdk::MediaFormat::Param aFormat,
+                        java::sdk::MediaFormat::Param aFormat,
                         MediaDataDecoderCallback* aCallback);
 
   virtual ~MediaCodecDataDecoder();
 
   RefPtr<MediaDataDecoder::InitPromise> Init() override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
@@ -68,60 +68,60 @@ protected:
     kStopping,
     kShutdown
   };
 
   friend class AndroidDecoderModule;
 
   static const char* ModuleStateStr(ModuleState aState);
 
-  virtual nsresult InitDecoder(widget::sdk::Surface::Param aSurface);
+  virtual nsresult InitDecoder(java::sdk::Surface::Param aSurface);
 
-  virtual nsresult Output(widget::sdk::BufferInfo::Param aInfo, void* aBuffer,
-      widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
+  virtual nsresult Output(java::sdk::BufferInfo::Param aInfo, void* aBuffer,
+      java::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
   {
     return NS_OK;
   }
 
-  virtual nsresult PostOutput(widget::sdk::BufferInfo::Param aInfo,
-      widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
+  virtual nsresult PostOutput(java::sdk::BufferInfo::Param aInfo,
+      java::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
   {
     return NS_OK;
   }
 
   virtual void Cleanup() {};
 
   nsresult ResetInputBuffers();
   nsresult ResetOutputBuffers();
 
   nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer);
   bool WaitForInput();
   already_AddRefed<MediaRawData> PeekNextSample();
   nsresult QueueSample(const MediaRawData* aSample);
   nsresult QueueEOS();
   void HandleEOS(int32_t aOutputStatus);
   Maybe<media::TimeUnit> GetOutputDuration();
-  nsresult ProcessOutput(widget::sdk::BufferInfo::Param aInfo,
-                         widget::sdk::MediaFormat::Param aFormat,
+  nsresult ProcessOutput(java::sdk::BufferInfo::Param aInfo,
+                         java::sdk::MediaFormat::Param aFormat,
                          int32_t aStatus);
   ModuleState State() const;
   // Sets decoder state and returns whether the new state has become effective.
   bool State(ModuleState aState);
   void DecoderLoop();
 
   virtual void ClearQueue();
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
-  widget::sdk::MediaFormat::GlobalRef mFormat;
+  java::sdk::MediaFormat::GlobalRef mFormat;
 
   MediaDataDecoderCallback* mCallback;
 
-  widget::sdk::MediaCodec::GlobalRef mDecoder;
+  java::sdk::MediaCodec::GlobalRef mDecoder;
 
   jni::ObjectArray::GlobalRef mInputBuffers;
   jni::ObjectArray::GlobalRef mOutputBuffers;
 
   nsCOMPtr<nsIThread> mThread;
 
   // Only these members are protected by mMonitor.
   Monitor mMonitor;
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -88,16 +88,17 @@ tags=capturestream
 [test_bug964376.html]
 [test_bug966247.html]
 tags=capturestream
 [test_bug972678.html]
 [test_bug1118372.html]
 [test_bug1027864.html]
 [test_bug1056032.html]
 skip-if = toolkit == 'android' # bug 1056706
+[test_bug1255618.html]
 [test_bug1267579.html]
 [test_channelMergerNode.html]
 [test_channelMergerNodeWithVolume.html]
 [test_channelSplitterNode.html]
 [test_channelSplitterNodeWithVolume.html]
 skip-if = (android_version == '18' && debug) # bug 1158417
 [test_convolverNode.html]
 [test_convolverNode_mono_mono.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_bug1255618.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test sync XHR does not crash unlinked AudioContext</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const filename = "test_bug1255618.html";
+
+function collect_and_fetch() {
+    SpecialPowers.forceGC();
+    SpecialPowers.forceCC();
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", filename, false);
+    var ended = false;
+    xhr.onloadend = function() { ended = true; }
+    // Sync XHR will suspend timeouts, which involves any AudioContexts still
+    // registered with the window.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1255618#c0
+    xhr.send(null);
+
+    ok(ended, "No crash during fetch");
+    SimpleTest.finish();
+}
+
+var ac = new AudioContext();
+
+ac.onstatechange = function () {
+    ac.onstatechange = null;
+    is(ac.state, "running", "statechange to running");
+    ac = null;
+    SimpleTest.executeSoon(collect_and_fetch);
+}
+
+</script>
+</body>
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -14,17 +14,16 @@
 #include "prmem.h"
 #include "nsContentUtils.h"
 
 #include "nsIFilePicker.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #ifdef MOZ_WIDGET_ANDROID
-#include "AndroidBridge.h"
 #include "nsISupportsUtils.h"
 #endif
 
 #ifdef MOZ_WEBRTC
 #include "YuvStamper.h"
 #endif
 
 #define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE
--- a/dom/mobilemessage/android/SmsManager.h
+++ b/dom/mobilemessage/android/SmsManager.h
@@ -5,17 +5,17 @@
 
 #ifndef SmsManager_h__
 #define SmsManager_h__
 
 #include "GeneratedJNINatives.h"
 
 namespace mozilla {
 
-class SmsManager : public widget::GeckoSmsManager::Natives<SmsManager>
+class SmsManager : public java::GeckoSmsManager::Natives<SmsManager>
 {
 private:
     SmsManager();
 
 public:
     static void NotifySmsReceived(int32_t aId,
                                   jni::String::Param aSender,
                                   jni::String::Param aBody,
--- a/dom/plugins/base/android/ANPEvent.cpp
+++ b/dom/plugins/base/android/ANPEvent.cpp
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "assert.h"
 #include "ANPBase.h"
 #include <android/log.h>
 #include "nsThreadUtils.h"
 #include "nsNPAPIPluginInstance.h"
-#include "AndroidBridge.h"
 #include "nsNPAPIPlugin.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_event_##name
 
 void
 anp_event_postEvent(NPP instance, const ANPEvent* event)
 {
--- a/dom/plugins/base/android/ANPNativeWindow.cpp
+++ b/dom/plugins/base/android/ANPNativeWindow.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: IDL; 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/. */
 
 // must include config.h first for webkit to fiddle with new/delete
 #include <android/log.h>
-#include "AndroidBridge.h"
 #include "ANPBase.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsNPAPIPluginInstance.h"
 #include "gfxRect.h"
 
 using namespace mozilla;
 
--- a/dom/plugins/base/android/ANPSystem.cpp
+++ b/dom/plugins/base/android/ANPSystem.cpp
@@ -1,22 +1,23 @@
 /* -*- Mode: IDL; 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 "base/basictypes.h"
 
+#include "ANPBase.h"
+#include "GeneratedJNIWrappers.h"
+#include "PluginPRLibrary.h"
 #include "assert.h"
-#include "ANPBase.h"
-#include <android/log.h>
 #include "nsNPAPIPluginInstance.h"
-#include "AndroidBridge.h"
 #include "nsNPAPIPlugin.h"
-#include "PluginPRLibrary.h"
+
+#include <android/log.h>
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_system_##name
 
 const char*
 anp_system_getApplicationDataDirectory(NPP instance)
 {
   static const char *dir = nullptr;
@@ -55,17 +56,17 @@ jclass anp_system_loadJavaClass(NPP inst
   LOG("%s", __PRETTY_FUNCTION__);
 
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
   mozilla::PluginPRLibrary* lib = static_cast<mozilla::PluginPRLibrary*>(pinst->GetPlugin()->GetLibrary());
 
   nsCString libName;
   lib->GetLibraryPath(libName);
 
-  return mozilla::widget::GeckoAppShell::LoadPluginClass(className, libName).Forget();
+  return mozilla::java::GeckoAppShell::LoadPluginClass(className, libName).Forget();
 }
 
 void anp_system_setPowerState(NPP instance, ANPPowerState powerState)
 {
   nsNPAPIPluginInstance* pinst = nsNPAPIPluginInstance::GetFromNPP(instance);
 
   if (pinst) {
     pinst->SetWakeLock(powerState == kScreenOn_ANPPowerState);
--- a/dom/plugins/base/android/ANPVideo.cpp
+++ b/dom/plugins/base/android/ANPVideo.cpp
@@ -1,14 +1,13 @@
 /* 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 <android/log.h>
-#include "AndroidBridge.h"
 #include "ANPBase.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsNPAPIPluginInstance.h"
 #include "gfxRect.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_video_##name
--- a/dom/plugins/base/android/ANPWindow.cpp
+++ b/dom/plugins/base/android/ANPWindow.cpp
@@ -2,17 +2,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/. */
 
 #include "base/basictypes.h"
 #include "assert.h"
 #include "ANPBase.h"
 #include <android/log.h>
-#include "AndroidBridge.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsWindow.h"
 #include "mozilla/dom/ScreenOrientation.h"
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_window_##name
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -96,17 +96,17 @@ using mozilla::plugins::PluginModuleCont
 #include "mozilla/a11y/Compatibility.h"
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #include "android_npapi.h"
 #include "ANPBase.h"
-#include "AndroidBridge.h"
+#include "GeneratedJNIWrappers.h"
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 #include "nsIAudioChannelAgent.h"
 #include "AudioChannelService.h"
 
 using namespace mozilla;
@@ -2110,17 +2110,17 @@ NPError
 
     case kSupportedDrawingModel_ANPGetValue: {
       LOG("get supported drawing model");
       return NPERR_GENERIC_ERROR;
     }
 
     case kJavaContext_ANPGetValue: {
       LOG("get java context");
-      auto ret = widget::GeckoAppShell::GetContext();
+      auto ret = java::GeckoAppShell::GetContext();
       if (!ret)
         return NPERR_GENERIC_ERROR;
 
       *static_cast<jobject*>(result) = ret.Forget();
       return NPERR_NO_ERROR;
     }
 
     case kAudioTrackInterfaceV1_ANPGetValue: {
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -783,17 +783,17 @@ void nsNPAPIPluginInstance::NotifyFullSc
 
   if (RUNNING != mRunning || mFullScreen == aFullScreen)
     return;
 
   mFullScreen = aFullScreen;
   SendLifecycleEvent(this, mFullScreen ? kEnterFullScreen_ANPLifecycleAction : kExitFullScreen_ANPLifecycleAction);
 
   if (mFullScreen && mFullScreenOrientation != dom::eScreenOrientation_None) {
-    widget::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation);
+    java::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation);
   }
 }
 
 void nsNPAPIPluginInstance::NotifySize(nsIntSize size)
 {
   if (kOpenGL_ANPDrawingModel != GetANPDrawingModel() ||
       size == mCurrentSize)
     return;
@@ -840,21 +840,21 @@ void nsNPAPIPluginInstance::SetFullScree
 
   uint32_t oldOrientation = mFullScreenOrientation;
   mFullScreenOrientation = orientation;
 
   if (mFullScreen) {
     // We're already fullscreen so immediately apply the orientation change
 
     if (mFullScreenOrientation != dom::eScreenOrientation_None) {
-      widget::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation);
+      java::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation);
     } else if (oldOrientation != dom::eScreenOrientation_None) {
       // We applied an orientation when we entered fullscreen, but
       // we don't want it anymore
-      widget::GeckoAppShell::UnlockScreenOrientation();
+      java::GeckoAppShell::UnlockScreenOrientation();
     }
   }
 }
 
 void nsNPAPIPluginInstance::PopPostedEvent(PluginEventRunnable* r)
 {
   mPostedEvents.RemoveElement(r);
 }
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1554,17 +1554,17 @@ bool nsPluginInstanceOwner::AddPluginVie
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
   if (!mInstance || !mJavaView)
     return;
 
-  widget::GeckoAppShell::RemovePluginView(
+  java::GeckoAppShell::RemovePluginView(
       jni::Object::Ref::From(jobject(mJavaView)), mFullScreen);
   jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView);
   mJavaView = nullptr;
 
   if (mFullScreen)
     sFullScreenInstance = nullptr;
 }
 
--- a/dom/system/android/AndroidLocationProvider.cpp
+++ b/dom/system/android/AndroidLocationProvider.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGeolocation.h"
 #include "nsGeoPosition.h"
-#include "AndroidBridge.h"
 #include "AndroidLocationProvider.h"
+#include "GeneratedJNIWrappers.h"
 
 using namespace mozilla;
 
 extern nsIGeolocationUpdate *gLocationCallback;
 
 NS_IMPL_ISUPPORTS(AndroidLocationProvider, nsIGeolocationProvider)
 
 AndroidLocationProvider::AndroidLocationProvider()
@@ -22,34 +22,34 @@ AndroidLocationProvider::AndroidLocation
 AndroidLocationProvider::~AndroidLocationProvider()
 {
     NS_IF_RELEASE(gLocationCallback);
 }
 
 NS_IMETHODIMP
 AndroidLocationProvider::Startup()
 {
-    widget::GeckoAppShell::EnableLocation(true);
+    java::GeckoAppShell::EnableLocation(true);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 AndroidLocationProvider::Watch(nsIGeolocationUpdate* aCallback)
 {
     NS_IF_RELEASE(gLocationCallback);
     gLocationCallback = aCallback;
     NS_IF_ADDREF(gLocationCallback);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 AndroidLocationProvider::Shutdown()
 {
-    widget::GeckoAppShell::EnableLocation(false);
+    java::GeckoAppShell::EnableLocation(false);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 AndroidLocationProvider::SetHighAccuracy(bool enable)
 {
-    widget::GeckoAppShell::EnableLocationHighAccuracy(enable);
+    java::GeckoAppShell::EnableLocationHighAccuracy(enable);
     return NS_OK;
 }
--- a/dom/system/android/nsHapticFeedback.cpp
+++ b/dom/system/android/nsHapticFeedback.cpp
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentChild.h"
 #include "nsHapticFeedback.h"
-#include "AndroidBridge.h"
+#include "GeneratedJNIWrappers.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS(nsHapticFeedback, nsIHapticFeedback)
 
 NS_IMETHODIMP
 nsHapticFeedback::PerformSimpleAction(int32_t aType)
 {
-    widget::GeckoAppShell::PerformHapticFeedback(aType == LongPress);
+    java::GeckoAppShell::PerformHapticFeedback(aType == LongPress);
     return NS_OK;
 }
--- a/dom/url/URL.cpp
+++ b/dom/url/URL.cpp
@@ -29,16 +29,40 @@ namespace mozilla {
 namespace dom {
 
 ///////////////////////////////////////////////////////////////////////////////
 // URL for main-thread
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace {
 
+template<typename T>
+void
+CreateObjectURLInternal(const GlobalObject& aGlobal, T aObject,
+                        nsAString& aResult, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal =
+    nsContentUtils::ObjectPrincipal(aGlobal.Get());
+
+  nsAutoCString url;
+  aRv = nsHostObjectProtocolHandler::AddDataEntry(aObject, principal, url);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  global->RegisterHostObjectURI(url);
+  CopyASCIItoUTF16(url, aResult);
+}
+
 // The URL implementation for the main-thread
 class URLMainThread final : public URL
 {
 public:
   static already_AddRefed<URLMainThread>
   Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
               URL& aBase, ErrorResult& aRv);
 
@@ -55,44 +79,34 @@ public:
               ErrorResult& aRv);
 
   static void
   CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
                   const objectURLOptions& aOptions, nsAString& aResult,
                   ErrorResult& aRv)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    CreateObjectURLInternal(aGlobal, aBlob.Impl(),
-                            NS_LITERAL_CSTRING(BLOBURI_SCHEME), aOptions,
-                            aResult, aRv);
+    CreateObjectURLInternal(aGlobal, aBlob.Impl(), aResult, aRv);
   }
 
   static void
   CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream,
                   const objectURLOptions& aOptions, nsAString& aResult,
                   ErrorResult& aRv)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    CreateObjectURLInternal(aGlobal, &aStream,
-                            NS_LITERAL_CSTRING(MEDIASTREAMURI_SCHEME), aOptions,
-                            aResult, aRv);
+    CreateObjectURLInternal(aGlobal, &aStream, aResult, aRv);
   }
 
   static void
   CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
                   const objectURLOptions& aOptions, nsAString& aResult,
                   ErrorResult& aRv);
 
   static void
-  CreateObjectURLInternal(const GlobalObject& aGlobal, nsISupports* aObject,
-                          const nsACString& aScheme,
-                          const objectURLOptions& aOptions,
-                          nsAString& aResult, ErrorResult& aRv);
-
-  static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
                   ErrorResult& aRv);
 
   URLMainThread(nsISupports* aParent, already_AddRefed<nsIURI> aURI)
     : URL(aParent)
     , mURI(aURI)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -241,62 +255,32 @@ URLMainThread::CreateObjectURL(const Glo
                                nsAString& aResult, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIPrincipal> principal =
     nsContentUtils::ObjectPrincipal(aGlobal.Get());
 
   nsAutoCString url;
-  aRv = nsHostObjectProtocolHandler::
-    AddDataEntry(NS_LITERAL_CSTRING(MEDIASOURCEURI_SCHEME),
-                 &aSource, principal, url);
+  aRv = nsHostObjectProtocolHandler::AddDataEntry(&aSource, principal, url);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsCOMPtr<nsIRunnable> revocation = NS_NewRunnableFunction(
     [url] {
       nsHostObjectProtocolHandler::RemoveDataEntry(url);
     });
 
   nsContentUtils::RunInStableState(revocation.forget());
 
   CopyASCIItoUTF16(url, aResult);
 }
 
 /* static */ void
-URLMainThread::CreateObjectURLInternal(const GlobalObject& aGlobal,
-                                       nsISupports* aObject,
-                                       const nsACString& aScheme,
-                                       const objectURLOptions& aOptions,
-                                       nsAString& aResult, ErrorResult& aRv)
-{
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!global) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsCOMPtr<nsIPrincipal> principal =
-    nsContentUtils::ObjectPrincipal(aGlobal.Get());
-
-  nsAutoCString url;
-  nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(aScheme, aObject,
-                                                          principal, url);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-    return;
-  }
-
-  global->RegisterHostObjectURI(url);
-  CopyASCIItoUTF16(url, aResult);
-}
-
-/* static */ void
 URLMainThread::RevokeObjectURL(const GlobalObject& aGlobal,
                                const nsAString& aURL, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
@@ -821,19 +805,18 @@ public:
 
     DebugOnly<bool> isMutable;
     MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable)));
     MOZ_ASSERT(!isMutable);
 
     nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
 
     nsAutoCString url;
-    nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(
-        NS_LITERAL_CSTRING(BLOBURI_SCHEME),
-        mBlobImpl, principal, url);
+    nsresult rv =
+      nsHostObjectProtocolHandler::AddDataEntry(mBlobImpl, principal, url);
 
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to add data entry for the blob!");
       SetDOMStringToNull(mURL);
       return false;
     }
 
     if (!mWorkerPrivate->IsSharedWorker() &&
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -255,16 +255,20 @@ WorkerRunnable::Run()
 
     Cancel();
 
     MOZ_ASSERT(mCallingCancelWithinRun);
     mCallingCancelWithinRun = false;
 
     MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
 
+    if (mBehavior == WorkerThreadModifyBusyCount) {
+      mWorkerPrivate->ModifyBusyCountFromWorker(false);
+    }
+
     return NS_OK;
   }
 
   bool result = PreRun(mWorkerPrivate);
   if (!result) {
     MOZ_ASSERT(targetIsWorkerThread,
                "The only PreRun implementation that can fail is "
                "ScriptExecutorRunnable");
--- a/extensions/spellcheck/hunspell/src/moz.build
+++ b/extensions/spellcheck/hunspell/src/moz.build
@@ -27,8 +27,12 @@ LOCAL_INCLUDES += [
     '../glue',
 ]
 
 # We allow warnings for third-party code that can be updated from upstream.
 ALLOW_COMPILER_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
+    CXXFLAGS += [
+        '-Wno-implicit-fallthrough',
+    ]
--- a/gfx/gl/AndroidSurfaceTexture.cpp
+++ b/gfx/gl/AndroidSurfaceTexture.cpp
@@ -15,18 +15,18 @@
 #include "nsThreadUtils.h"
 #include "mozilla/gfx/Matrix.h"
 #include "GeneratedJNIWrappers.h"
 #include "SurfaceTexture.h"
 #include "GLContext.h"
 
 using namespace mozilla;
 using namespace mozilla::jni;
-using namespace mozilla::widget;
-using namespace mozilla::widget::sdk;
+using namespace mozilla::java;
+using namespace mozilla::java::sdk;
 
 namespace mozilla {
 namespace gl {
 
 // Maintains a mapping between AndroidSurfaceTexture instances and their
 // unique numerical IDs. [thread-safe]
 class InstanceMap
 {
--- a/gfx/gl/AndroidSurfaceTexture.h
+++ b/gfx/gl/AndroidSurfaceTexture.h
@@ -78,27 +78,27 @@ public:
   // if the upstream callback is received on a different thread
   void SetFrameAvailableCallback(nsIRunnable* aRunnable);
 
   // Only should be called by AndroidJNI when we get a
   // callback from the underlying SurfaceTexture instance
   void NotifyFrameAvailable();
 
   GLuint Texture() const { return mTexture; }
-  const widget::sdk::Surface::Ref& JavaSurface() const { return mSurface; }
+  const java::sdk::Surface::Ref& JavaSurface() const { return mSurface; }
 
 private:
   AndroidSurfaceTexture();
   ~AndroidSurfaceTexture();
 
   bool Init(GLContext* aContext, GLuint aTexture);
 
   GLuint mTexture;
-  widget::sdk::SurfaceTexture::GlobalRef mSurfaceTexture;
-  widget::sdk::Surface::GlobalRef mSurface;
+  java::sdk::SurfaceTexture::GlobalRef mSurfaceTexture;
+  java::sdk::Surface::GlobalRef mSurface;
 
   GLContext* mAttachedContext;
 
   RefPtr<AndroidNativeWindow> mNativeWindow;
   int mID;
   nsCOMPtr<nsIRunnable> mFrameAvailableCallback;
 
   mutable Monitor mMonitor;
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -18,17 +18,17 @@ class CompositorWidgetDelegate;
 } // namespace widget
 namespace gfx {
 class GPUProcessHost;
 class GPUProcessManager;
 } // namespace gfx
 namespace layers {
 
 class GeckoContentController;
-class APZCTreeManager;
+class IAPZCTreeManager;
 class CompositorBridgeParent;
 class CompositorBridgeChild;
 class ClientLayerManager;
 
 // A CompositorSession provides access to a compositor without exposing whether
 // or not it's in-process or out-of-process.
 class CompositorSession
 {
@@ -46,17 +46,17 @@ public:
 
   // This returns a CompositorBridgeParent if the compositor resides in the same process.
   virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
 
   // Set the GeckoContentController for the root of the layer tree.
   virtual void SetContentController(GeckoContentController* aController) = 0;
 
   // Return the Async Pan/Zoom Tree Manager for this compositor.
-  virtual already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const = 0;
+  virtual already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
 
   // Return the child end of the compositor IPC bridge.
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   // Return the proxy for accessing the compositor's widget.
   CompositorWidgetDelegate* GetCompositorWidgetDelegate() {
     return mCompositorWidgetDelegate;
   }
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -478,17 +478,17 @@ GPUProcessManager::CreateContentVRManage
       return false;
     }
   }
 
   *aOutEndpoint = Move(childPipe);
   return true;
 }
 
-already_AddRefed<APZCTreeManager>
+already_AddRefed<IAPZCTreeManager>
 GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
 {
   return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
 }
 
 uint64_t
 GPUProcessManager::AllocateLayerTreeId()
 {
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -18,17 +18,17 @@
 #include "mozilla/ipc/Transport.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 class nsBaseWidget;
 
 
 namespace mozilla {
 namespace layers {
-class APZCTreeManager;
+class IAPZCTreeManager;
 class CompositorSession;
 class ClientLayerManager;
 class CompositorUpdateObserver;
 class PCompositorBridgeChild;
 class PImageBridgeChild;
 } // namespace layers
 namespace widget {
 class CompositorWidget;
@@ -47,19 +47,19 @@ class VsyncBridgeChild;
 class VsyncIOThreadHolder;
 class PVRManagerChild;
 
 // The GPUProcessManager is a singleton responsible for creating GPU-bound
 // objects that may live in another process. Currently, it provides access
 // to the compositor via CompositorBridgeParent.
 class GPUProcessManager final : public GPUProcessHost::Listener
 {
-  typedef layers::APZCTreeManager APZCTreeManager;
   typedef layers::ClientLayerManager ClientLayerManager;
   typedef layers::CompositorSession CompositorSession;
+  typedef layers::IAPZCTreeManager IAPZCTreeManager;
   typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
   typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
   typedef layers::PImageBridgeChild PImageBridgeChild;
 
 public:
   static void Initialize();
   static void Shutdown();
   static GPUProcessManager* Get();
@@ -86,17 +86,17 @@ public:
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
   bool CreateContentVRManager(base::ProcessId aOtherProcess,
                               ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
-  already_AddRefed<APZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
+  already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Allocate an ID that can be used to refer to a layer tree and
   // associated resources that live only on the compositor thread.
   //
   // Must run on the content main thread.
   uint64_t AllocateLayerTreeId();
 
   // Release compositor-thread resources referred to by |aID|.
--- a/gfx/ipc/InProcessCompositorSession.cpp
+++ b/gfx/ipc/InProcessCompositorSession.cpp
@@ -1,16 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 "InProcessCompositorSession.h"
 
+// so we can cast an APZCTreeManager to an IAPZCTreeManager
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+
 namespace mozilla {
 namespace layers {
 
 InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidget* aWidget,
                                                        CompositorBridgeChild* aChild,
                                                        CompositorBridgeParent* aParent)
  : CompositorSession(aWidget->AsDelegate(), aChild, aParent->RootLayerTreeId()),
    mCompositorBridgeParent(aParent),
@@ -45,17 +49,17 @@ InProcessCompositorSession::GetInProcess
 }
 
 void
 InProcessCompositorSession::SetContentController(GeckoContentController* aController)
 {
   mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController);
 }
 
-already_AddRefed<APZCTreeManager>
+already_AddRefed<IAPZCTreeManager>
 InProcessCompositorSession::GetAPZCTreeManager() const
 {
   return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
 }
 
 void
 InProcessCompositorSession::Shutdown()
 {
--- a/gfx/ipc/InProcessCompositorSession.h
+++ b/gfx/ipc/InProcessCompositorSession.h
@@ -24,17 +24,17 @@ public:
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
-  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
+  already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
 private:
   InProcessCompositorSession(widget::CompositorWidget* aWidget,
                              CompositorBridgeChild* aChild,
                              CompositorBridgeParent* aParent);
 
 private:
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -25,17 +25,17 @@ RemoteCompositorSession::GetInProcessBri
 }
 
 void
 RemoteCompositorSession::SetContentController(GeckoContentController* aController)
 {
   MOZ_CRASH("NYI");
 }
 
-already_AddRefed<APZCTreeManager>
+already_AddRefed<IAPZCTreeManager>
 RemoteCompositorSession::GetAPZCTreeManager() const
 {
   return nullptr;
 }
 
 void
 RemoteCompositorSession::Shutdown()
 {
--- a/gfx/ipc/RemoteCompositorSession.h
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -17,17 +17,17 @@ class RemoteCompositorSession final : pu
 {
 public:
   RemoteCompositorSession(CompositorBridgeChild* aChild,
                           CompositorWidgetDelegate* aWidgetDelegate,
                           const uint64_t& aRootLayerTreeId);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
-  already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
+  already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/IAPZCTreeManager.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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/layers/IAPZCTreeManager.h"
+
+#include "gfxPrefs.h"                       // for gfxPrefs
+#include "InputData.h"                      // for InputData, etc
+#include "mozilla/EventStateManager.h"      // for WheelPrefs
+#include "mozilla/layers/APZThreadUtils.h"  // for AssertOnCompositorThread, etc
+#include "mozilla/MouseEvents.h"            // for WidgetMouseEvent
+#include "mozilla/TouchEvents.h"            // for WidgetTouchEvent
+
+namespace mozilla {
+namespace layers {
+
+static bool
+WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
+{
+  return aEvent.mMessage == eMouseMove ||
+         aEvent.mMessage == eMouseDown ||
+         aEvent.mMessage == eMouseUp ||
+         aEvent.mMessage == eDragEnd;
+}
+
+// Returns whether or not a wheel event action will be (or was) performed by
+// APZ. If this returns true, the event must not perform a synchronous
+// scroll.
+//
+// Even if this returns false, all wheel events in APZ-aware widgets must
+// be sent through APZ so they are transformed correctly for TabParent.
+static bool
+WillHandleWheelEvent(WidgetWheelEvent* aEvent)
+{
+  return EventStateManager::WheelEventIsScrollAction(aEvent) &&
+         (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
+          aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
+          aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
+}
+
+nsEventStatus
+IAPZCTreeManager::ReceiveInputEvent(
+    WidgetInputEvent& aEvent,
+    ScrollableLayerGuid* aOutTargetGuid,
+    uint64_t* aOutInputBlockId)
+{
+  APZThreadUtils::AssertOnControllerThread();
+
+  // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+  // it if the input event goes into a block.
+  if (aOutInputBlockId) {
+    *aOutInputBlockId = 0;
+  }
+
+  switch (aEvent.mClass) {
+    case eMouseEventClass:
+    case eDragEventClass: {
+
+      WidgetMouseEvent& mouseEvent = *aEvent.AsMouseEvent();
+
+      // Note, we call this before having transformed the reference point.
+      if (mouseEvent.IsReal()) {
+        UpdateWheelTransaction(mouseEvent.mRefPoint, mouseEvent.mMessage);
+      }
+
+      if (WillHandleMouseEvent(mouseEvent)) {
+
+        MouseInput input(mouseEvent);
+        input.mOrigin = ScreenPoint(mouseEvent.mRefPoint.x, mouseEvent.mRefPoint.y);
+
+        nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+
+        mouseEvent.mRefPoint.x = input.mOrigin.x;
+        mouseEvent.mRefPoint.y = input.mOrigin.y;
+        mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+        return status;
+
+      }
+
+      TransformEventRefPoint(&mouseEvent.mRefPoint, aOutTargetGuid);
+      return nsEventStatus_eIgnore;
+    }
+    case eTouchEventClass: {
+
+      WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
+      MultiTouchInput touchInput(touchEvent);
+      nsEventStatus result = ReceiveInputEvent(touchInput, aOutTargetGuid, aOutInputBlockId);
+      // touchInput was modified in-place to possibly remove some
+      // touch points (if we are overscrolled), and the coordinates were
+      // modified using the APZ untransform. We need to copy these changes
+      // back into the WidgetInputEvent.
+      touchEvent.mTouches.Clear();
+      touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
+      for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
+        *touchEvent.mTouches.AppendElement() =
+          touchInput.mTouches[i].ToNewDOMTouch();
+      }
+      touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
+      return result;
+
+    }
+    case eWheelEventClass: {
+      WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
+
+      if (WillHandleWheelEvent(&wheelEvent)) {
+
+        ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
+        if (gfxPrefs::SmoothScrollEnabled() &&
+            ((wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
+              gfxPrefs::WheelSmoothScrollEnabled()) ||
+             (wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE &&
+              gfxPrefs::PageSmoothScrollEnabled())))
+        {
+          scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
+        }
+
+        ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
+        ScrollWheelInput input(wheelEvent.mTime, wheelEvent.mTimeStamp, 0,
+                               scrollMode,
+                               ScrollWheelInput::DeltaTypeForDeltaMode(
+                                                   wheelEvent.mDeltaMode),
+                               origin,
+                               wheelEvent.mDeltaX, wheelEvent.mDeltaY,
+                               wheelEvent.mAllowToOverrideSystemScrollSpeed);
+
+        // We add the user multiplier as a separate field, rather than premultiplying
+        // it, because if the input is converted back to a WidgetWheelEvent, then
+        // EventStateManager would apply the delta a second time. We could in theory
+        // work around this by asking ESM to customize the event much sooner, and
+        // then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
+        // now, this seems easier.
+        EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
+          &input.mUserDeltaMultiplierX,
+          &input.mUserDeltaMultiplierY);
+
+        nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+        wheelEvent.mRefPoint.x = input.mOrigin.x;
+        wheelEvent.mRefPoint.y = input.mOrigin.y;
+        wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+        return status;
+      }
+
+      UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+      TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+      return nsEventStatus_eIgnore;
+
+    }
+    default: {
+
+      UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+      TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+      return nsEventStatus_eIgnore;
+
+    }
+  }
+
+  MOZ_ASSERT_UNREACHABLE("Invalid WidgetInputEvent type.");
+  return nsEventStatus_eConsumeNoDefault;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/IAPZCTreeManager.h
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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_layers_IAPZCTreeManager_h
+#define mozilla_layers_IAPZCTreeManager_h
+
+#include <stdint.h>                     // for uint64_t, uint32_t
+
+#include "FrameMetrics.h"               // for FrameMetrics, etc
+#include "mozilla/EventForwards.h"      // for WidgetInputEvent, nsEventStatus
+#include "mozilla/layers/APZUtils.h"    // for HitTestResult
+#include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
+#include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
+#include "Units.h"                      // for CSSPoint, CSSRect, etc
+
+namespace mozilla {
+class InputData;
+
+namespace layers {
+
+enum AllowedTouchBehavior {
+  NONE =               0,
+  VERTICAL_PAN =       1 << 0,
+  HORIZONTAL_PAN =     1 << 1,
+  PINCH_ZOOM =         1 << 2,
+  DOUBLE_TAP_ZOOM =    1 << 3,
+  UNKNOWN =            1 << 4
+};
+
+enum ZoomToRectBehavior : uint32_t {
+  DEFAULT_BEHAVIOR =   0,
+  DISABLE_ZOOM_OUT =   1 << 0,
+  PAN_INTO_VIEW_ONLY = 1 << 1,
+  ONLY_ZOOM_TO_DEFAULT_SCALE  = 1 << 2
+};
+
+class AsyncDragMetrics;
+
+class IAPZCTreeManager {
+  NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
+
+public:
+
+  /**
+   * General handler for incoming input events. Manipulates the frame metrics
+   * based on what type of input it is. For example, a PinchGestureEvent will
+   * cause scaling. This should only be called externally to this class, and
+   * must be called on the controller thread.
+   *
+   * This function transforms |aEvent| to have its coordinates in DOM space.
+   * This is so that the event can be passed through the DOM and content can
+   * handle them. The event may need to be converted to a WidgetInputEvent
+   * by the caller if it wants to do this.
+   *
+   * The following values may be returned by this function:
+   * nsEventStatus_eConsumeNoDefault is returned to indicate the
+   *   APZ is consuming this event and the caller should discard the event with
+   *   extreme prejudice. The exact scenarios under which this is returned is
+   *   implementation-dependent and may vary.
+   * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
+   *   use this event. This might be because it was directed at a point on
+   *   the screen where there was no APZ, or because the thing the user was
+   *   trying to do was not allowed. (For example, attempting to pan a
+   *   non-pannable document).
+   * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
+   *   code may have used this event to do some user-visible thing. Note that
+   *   in some cases CONSUMED is returned even if the event was NOT used. This
+   *   is because we cannot always know at the time of event delivery whether
+   *   the event will be used or not. So we err on the side of sending
+   *   CONSUMED when we are uncertain.
+   *
+   * @param aEvent input event object; is modified in-place
+   * @param aOutTargetGuid returns the guid of the apzc this event was
+   * delivered to. May be null.
+   * @param aOutInputBlockId returns the id of the input block that this event
+   * was added to, if that was the case. May be null.
+   */
+  virtual nsEventStatus ReceiveInputEvent(
+      InputData& aEvent,
+      ScrollableLayerGuid* aOutTargetGuid,
+      uint64_t* aOutInputBlockId) = 0;
+
+  /**
+   * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an
+   * already-existing instance of an WidgetInputEvent which may be an
+   * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the
+   * event can be passed through the DOM and content can handle them.
+   *
+   * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be
+   * called on the main thread. See widget/InputData.h for more information on
+   * why we have InputData and WidgetInputEvent separated. If this function is
+   * used, the controller thread must be the main thread, or undefined behaviour
+   * may occur.
+   * NOTE: On unix, mouse events are treated as touch and are forwarded
+   * to the appropriate apz as such.
+   *
+   * See documentation for other ReceiveInputEvent above.
+   */
+  nsEventStatus ReceiveInputEvent(
+      WidgetInputEvent& aEvent,
+      ScrollableLayerGuid* aOutTargetGuid,
+      uint64_t* aOutInputBlockId);
+
+  /**
+   * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+   * in. The actual animation is done on the compositor thread after being set
+   * up. |aRect| must be given in CSS pixels, relative to the document.
+   * |aFlags| is a combination of the ZoomToRectBehavior enum values.
+   */
+  virtual void ZoomToRect(
+      const ScrollableLayerGuid& aGuid,
+      const CSSRect& aRect,
+      const uint32_t aFlags = DEFAULT_BEHAVIOR) = 0;
+
+  /**
+   * If we have touch listeners, this should always be called when we know
+   * definitively whether or not content has preventDefaulted any touch events
+   * that have come in. If |aPreventDefault| is true, any touch events in the
+   * queue will be discarded. This function must be called on the controller
+   * thread.
+   */
+  virtual void ContentReceivedInputBlock(
+      uint64_t aInputBlockId,
+      bool aPreventDefault) = 0;
+
+  /**
+   * When the event regions code is enabled, this function should be invoked to
+   * to confirm the target of the input block. This is only needed in cases
+   * where the initial input event of the block hit a dispatch-to-content region
+   * but is safe to call for all input blocks. This function should always be
+   * invoked on the controller thread.
+   * The different elements in the array of targets correspond to the targets
+   * for the different touch points. In the case where the touch point has no
+   * target, or the target is not a scrollable frame, the target's |mScrollId|
+   * should be set to FrameMetrics::NULL_SCROLL_ID.
+   */
+  virtual void SetTargetAPZC(
+      uint64_t aInputBlockId,
+      const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+
+  /**
+   * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+   * If the |aConstraints| is Nothing() then previously-provided constraints for
+   * the given |aGuid| are cleared.
+   */
+  virtual void UpdateZoomConstraints(
+      const ScrollableLayerGuid& aGuid,
+      const Maybe<ZoomConstraints>& aConstraints) = 0;
+
+  /**
+   * Cancels any currently running animation. Note that all this does is set the
+   * state of the AsyncPanZoomController back to NOTHING, but it is the
+   * animation's responsibility to check this before advancing.
+   */
+  virtual void CancelAnimation(const ScrollableLayerGuid &aGuid) = 0;
+
+  /**
+   * Adjusts the root APZC to compensate for a shift in the surface. See the
+   * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
+   * some more details. This is only currently needed due to surface shifts
+   * caused by the dynamic toolbar on Android.
+   */
+  virtual void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) = 0;
+
+  virtual void SetDPI(float aDpiValue) = 0;
+
+  /**
+   * Sets allowed touch behavior values for current touch-session for specific
+   * input block (determined by aInputBlock).
+   * Should be invoked by the widget. Each value of the aValues arrays
+   * corresponds to the different touch point that is currently active.
+   * Must be called after receiving the TOUCH_START event that starts the
+   * touch-session.
+   * This must be called on the controller thread.
+   */
+  virtual void SetAllowedTouchBehavior(
+      uint64_t aInputBlockId,
+      const nsTArray<TouchBehaviorFlags>& aValues) = 0;
+
+  virtual void StartScrollbarDrag(
+      const ScrollableLayerGuid& aGuid,
+      const AsyncDragMetrics& aDragMetrics) = 0;
+
+  /**
+   * Function used to disable LongTap gestures.
+   *
+   * On slow running tests, drags and touch events can be misinterpreted
+   * as a long tap. This allows tests to disable long tap gesture detection.
+   */
+  virtual void SetLongTapEnabled(bool aTapGestureEnabled) = 0;
+
+  /**
+   * Process touch velocity.
+   * Sometimes the touch move event will have a velocity even though no scrolling
+   * is occurring such as when the toolbar is being hidden/shown in Fennec.
+   * This function can be called to have the y axis' velocity queue updated.
+   */
+  virtual void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) = 0;
+
+protected:
+
+  // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
+
+  virtual void TransformEventRefPoint(
+      LayoutDeviceIntPoint* aRefPoint,
+      ScrollableLayerGuid* aOutTargetGuid) = 0;
+
+  virtual void UpdateWheelTransaction(
+      LayoutDeviceIntPoint aRefPoint,
+      EventMessage aEventMessage) = 0;
+
+  // Discourage destruction outside of decref
+
+  virtual ~IAPZCTreeManager() { }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_IAPZCTreeManager_h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -600,40 +600,16 @@ APZCTreeManager::UpdateHitTestingTree(Tr
     gfx::TreeAutoIndent indent(mApzcTreeLog);
     next = UpdateHitTestingTree(aState, child, childLayersId,
                                 ancestorTransform, aParent, next);
   }
 
   return node;
 }
 
-// Returns whether or not a wheel event action will be (or was) performed by
-// APZ. If this returns true, the event must not perform a synchronous
-// scroll.
-//
-// Even if this returns false, all wheel events in APZ-aware widgets must
-// be sent through APZ so they are transformed correctly for TabParent.
-static bool
-WillHandleWheelEvent(WidgetWheelEvent* aEvent)
-{
-  return EventStateManager::WheelEventIsScrollAction(aEvent) &&
-         (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
-          aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
-          aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
-}
-
-static bool
-WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
-{
-  return aEvent.mMessage == eMouseMove ||
-         aEvent.mMessage == eMouseDown ||
-         aEvent.mMessage == eMouseUp ||
-         aEvent.mMessage == eDragEnd;
-}
-
 template<typename PanGestureOrScrollWheelInput>
 static bool
 WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
 {
   if (!NS_IsMainThread()) {
     return true;
   }
 
@@ -884,16 +860,19 @@ APZCTreeManager::ReceiveInputEvent(Input
             /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
             tapInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         tapInput.mPoint = *untransformedPoint;
       }
       break;
+    } case SENTINEL_INPUT: {
+      MOZ_ASSERT_UNREACHABLE("Invalid InputType.");
+      break;
     }
   }
   return result;
 }
 
 static TouchBehaviorFlags
 ConvertToTouchBehavior(HitTestResult result)
 {
@@ -1034,17 +1013,17 @@ APZCTreeManager::ProcessTouchInput(Multi
     }
 
     // For computing the event to pass back to Gecko, use up-to-date transforms
     // (i.e. not anything cached in an input block).
     // This ensures that transformToApzc and transformToGecko are in sync.
     ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
     ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
     ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
-    
+
     for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
       SingleTouchData& touchData = aInput.mTouches[i];
       Maybe<ScreenIntPoint> untransformedScreenPoint = UntransformBy(
           outTransform, touchData.mScreenPoint);
       if (!untransformedScreenPoint) {
         return nsEventStatus_eIgnore;
       }
       touchData.mScreenPoint = *untransformedScreenPoint;
@@ -1060,42 +1039,41 @@ APZCTreeManager::ProcessTouchInput(Multi
     mHitResultForInputBlock = HitNothing;
     mRetainedTouchIdentifier = -1;
   }
 
   return result;
 }
 
 void
-APZCTreeManager::UpdateWheelTransaction(WidgetInputEvent& aEvent)
+APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+                                        EventMessage aEventMessage)
 {
   WheelBlockState* txn = mInputQueue->GetCurrentWheelTransaction();
   if (!txn) {
     return;
   }
 
   // If the transaction has simply timed out, we don't need to do anything
   // else.
   if (txn->MaybeTimeout(TimeStamp::Now())) {
     return;
   }
 
-  switch (aEvent.mMessage) {
+  switch (aEventMessage) {
    case eMouseMove:
    case eDragOver: {
-     WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent();
-     if (!mouseEvent->IsReal()) {
-       return;
-     }
 
-     ScreenIntPoint point =
-       ViewAs<ScreenPixel>(aEvent.mRefPoint,
-         PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
-     txn->OnMouseMove(point);
-     return;
+    ScreenIntPoint point =
+     ViewAs<ScreenPixel>(aRefPoint,
+       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+
+    txn->OnMouseMove(point);
+
+    return;
    }
    case eKeyPress:
    case eKeyUp:
    case eKeyDown:
    case eMouseUp:
    case eMouseDown:
    case eMouseDoubleClick:
    case eMouseClick:
@@ -1103,177 +1081,50 @@ APZCTreeManager::UpdateWheelTransaction(
    case eDrop:
      txn->EndTransaction();
      return;
    default:
      break;
   }
 }
 
-nsEventStatus
-APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
-                              ScrollableLayerGuid* aOutTargetGuid,
-                              uint64_t* aOutInputBlockId)
+void
+APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
+                              ScrollableLayerGuid* aOutTargetGuid)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  nsEventStatus result = nsEventStatus_eIgnore;
-
-  // Note, we call this before having transformed the reference point.
-  UpdateWheelTransaction(aEvent);
-
-  // Transform the mRefPoint.
+  // Transform the aRefPoint.
   // If the event hits an overscrolled APZC, instruct the caller to ignore it.
   HitTestResult hitResult = HitNothing;
   PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
   ScreenIntPoint refPointAsScreen =
-    ViewAs<ScreenPixel>(aEvent.mRefPoint, LDIsScreen);
+    ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
   if (apzc) {
     MOZ_ASSERT(hitResult != HitNothing);
     apzc->GetGuid(aOutTargetGuid);
     ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
     ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
     ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
     Maybe<ScreenIntPoint> untransformedRefPoint =
       UntransformBy(outTransform, refPointAsScreen);
     if (untransformedRefPoint) {
-      aEvent.mRefPoint =
+      *aRefPoint =
         ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
     }
   }
-  return result;
-}
-
-nsEventStatus
-APZCTreeManager::ProcessMouseEvent(WidgetMouseEventBase& aEvent,
-                                   ScrollableLayerGuid* aOutTargetGuid,
-                                   uint64_t* aOutInputBlockId)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Note, we call this before having transformed the reference point.
-  UpdateWheelTransaction(aEvent);
-
-  MouseInput input(aEvent);
-  input.mOrigin = ScreenPoint(aEvent.mRefPoint.x, aEvent.mRefPoint.y);
-
-  nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
-
-  aEvent.mRefPoint.x = input.mOrigin.x;
-  aEvent.mRefPoint.y = input.mOrigin.y;
-  aEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
-  return status;
 }
 
 void
 APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
 {
   if (mApzcForInputBlock) {
     mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
   }
 }
 
-nsEventStatus
-APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
-                                   ScrollableLayerGuid* aOutTargetGuid,
-                                   uint64_t* aOutInputBlockId)
-{
-  ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
-  if (gfxPrefs::SmoothScrollEnabled() &&
-      ((aEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
-        gfxPrefs::WheelSmoothScrollEnabled()) ||
-       (aEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE &&
-        gfxPrefs::PageSmoothScrollEnabled())))
-  {
-    scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
-  }
-
-  ScreenPoint origin(aEvent.mRefPoint.x, aEvent.mRefPoint.y);
-  ScrollWheelInput input(aEvent.mTime, aEvent.mTimeStamp, 0,
-                         scrollMode,
-                         ScrollWheelInput::DeltaTypeForDeltaMode(
-                                             aEvent.mDeltaMode),
-                         origin,
-                         aEvent.mDeltaX, aEvent.mDeltaY,
-                         aEvent.mAllowToOverrideSystemScrollSpeed);
-
-  // We add the user multiplier as a separate field, rather than premultiplying
-  // it, because if the input is converted back to a WidgetWheelEvent, then
-  // EventStateManager would apply the delta a second time. We could in theory
-  // work around this by asking ESM to customize the event much sooner, and
-  // then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
-  // now, this seems easier.
-  EventStateManager::GetUserPrefsForWheelEvent(&aEvent,
-    &input.mUserDeltaMultiplierX,
-    &input.mUserDeltaMultiplierY);
-
-  nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
-  aEvent.mRefPoint.x = input.mOrigin.x;
-  aEvent.mRefPoint.y = input.mOrigin.y;
-  aEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
-  return status;
-}
-
-nsEventStatus
-APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
-                                   ScrollableLayerGuid* aOutTargetGuid,
-                                   uint64_t* aOutInputBlockId)
-{
-  // In general it is preferable to use the version of ReceiveInputEvent
-  // that takes an InputData, as that is usable from off-main-thread. On some
-  // platforms OMT input isn't possible, and there we can use this version.
-
-  MOZ_ASSERT(NS_IsMainThread());
-  APZThreadUtils::AssertOnControllerThread();
-
-  // Initialize aOutInputBlockId to a sane value, and then later we overwrite
-  // it if the input event goes into a block.
-  if (aOutInputBlockId) {
-    *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
-  }
-
-  switch (aEvent.mClass) {
-    case eMouseEventClass:
-    case eDragEventClass: {
-      WidgetMouseEventBase& mouseEvent = *aEvent.AsMouseEventBase();
-      if (WillHandleMouseEvent(mouseEvent)) {
-        return ProcessMouseEvent(mouseEvent, aOutTargetGuid, aOutInputBlockId);
-      }
-      return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
-    }
-    case eTouchEventClass: {
-      WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
-      MultiTouchInput touchInput(touchEvent);
-      nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
-      // touchInput was modified in-place to possibly remove some
-      // touch points (if we are overscrolled), and the coordinates were
-      // modified using the APZ untransform. We need to copy these changes
-      // back into the WidgetInputEvent.
-      touchEvent.mTouches.Clear();
-      touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
-      for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
-        *touchEvent.mTouches.AppendElement() =
-          touchInput.mTouches[i].ToNewDOMTouch();
-      }
-      touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
-      return result;
-    }
-    case eWheelEventClass: {
-      WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
-      if (WillHandleWheelEvent(&wheelEvent)) {
-        return ProcessWheelEvent(wheelEvent, aOutTargetGuid, aOutInputBlockId);
-      }
-      return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
-    }
-    default: {
-      return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
-    }
-  }
-}
-
 void
 APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
                             const CSSRect& aRect,
                             const uint32_t aFlags)
 {
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (apzc) {
     apzc->ZoomToRect(aRect, aFlags);
@@ -1757,17 +1608,17 @@ APZCTreeManager::BuildOverscrollHandoffC
   // Print the overscroll chain for debugging.
   for (uint32_t i = 0; i < result->Length(); ++i) {
     APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
   }
 
   return result;
 }
 
-/* static */ void
+void
 APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
 {
   APZThreadUtils::RunOnControllerThread(
     NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
 }
 
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics)
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -1,60 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; 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_layers_APZCTreeManager_h
 #define mozilla_layers_APZCTreeManager_h
 
-#include <stdint.h>                     // for uint64_t, uint32_t
 #include <map>                          // for std::map
 
-#include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "gfxPoint.h"                   // for gfxPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
-#include "mozilla/EventForwards.h"      // for WidgetInputEvent, nsEventStatus
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
-#include "mozilla/layers/APZUtils.h"    // for HitTestResult
 #include "mozilla/layers/TouchCounter.h"// for TouchCounter
+#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
 #include "mozilla/Mutex.h"              // for Mutex
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for mozilla::TimeStamp
 #include "nsCOMPtr.h"                   // for already_AddRefed
-#include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
-#include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
-#include "Units.h"                      // for CSSPoint, CSSRect, etc
+
 
 namespace mozilla {
-class InputData;
 class MultiTouchInput;
 
 namespace layers {
 
-enum AllowedTouchBehavior {
-  NONE =               0,
-  VERTICAL_PAN =       1 << 0,
-  HORIZONTAL_PAN =     1 << 1,