Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 23 Oct 2013 14:59:15 +0200
changeset 165635 4b26a2531e86f7c2cacc4d9a0d8255122d36edd6
parent 165634 2d4d0a353a839e6c9860d96ddfb6bcb1798d8204 (current diff)
parent 165602 8803e8c0ee3efe4c0c07be612ce51c1d274b7c96 (diff)
child 165636 443c255b048a21b3bd80c044ed6882e904c44aac
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to b2g-inbound
accessible/src/base/RoleAsserts.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 926091 says hello to bug 924992 by touching webidl
+Bug 918207 needed a clobber on every platform because we can't have nice things
--- a/accessible/src/atk/AccessibleWrap.cpp
+++ b/accessible/src/atk/AccessibleWrap.cpp
@@ -866,66 +866,58 @@ refStateSetCB(AtkObject *aAtkObj)
     }
 
     // Map states
     TranslateStates(accWrap->State(), state_set);
 
     return state_set;
 }
 
+static void
+UpdateAtkRelation(RelationType aType, Accessible* aAcc,
+                  AtkRelationType aAtkType, AtkRelationSet* aAtkSet)
+{
+  if (aAtkType == ATK_RELATION_NULL)
+    return;
+
+  AtkRelation* atkRelation =
+    atk_relation_set_get_relation_by_type(aAtkSet, aAtkType);
+  if (atkRelation)
+    atk_relation_set_remove(aAtkSet, atkRelation);
+
+  Relation rel(aAcc->RelationByType(aType));
+  nsTArray<AtkObject*> targets;
+  Accessible* tempAcc = nullptr;
+  while ((tempAcc = rel.Next()))
+    targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc));
+
+  if (targets.Length()) {
+    atkRelation = atk_relation_new(targets.Elements(),
+                                   targets.Length(), aAtkType);
+    atk_relation_set_add(aAtkSet, atkRelation);
+    g_object_unref(atkRelation);
+  }
+}
+
 AtkRelationSet *
 refRelationSetCB(AtkObject *aAtkObj)
 {
   AtkRelationSet* relation_set =
     ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
 
   AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   if (!accWrap)
     return relation_set;
 
-  // Keep in sync with AtkRelationType enum.
-  static const RelationType relationTypes[] = {
-    RelationType::CONTROLLED_BY,
-    RelationType::CONTROLLER_FOR,
-    RelationType::LABEL_FOR,
-    RelationType::LABELLED_BY,
-    RelationType::MEMBER_OF,
-    RelationType::NODE_CHILD_OF,
-    RelationType::FLOWS_TO,
-    RelationType::FLOWS_FROM,
-    RelationType::SUBWINDOW_OF,
-    RelationType::EMBEDS,
-    RelationType::EMBEDDED_BY,
-    RelationType::POPUP_FOR,
-    RelationType::PARENT_WINDOW_OF,
-    RelationType::DESCRIBED_BY,
-    RelationType::DESCRIPTION_FOR,
-    RelationType::NODE_PARENT_OF
-  };
+#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
+  UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set);
 
-  for (uint32_t i = 0; i < ArrayLength(relationTypes); i++) {
-    // Shift to 1 to skip ATK_RELATION_NULL.
-    AtkRelationType atkType = static_cast<AtkRelationType>(i + 1);
-    AtkRelation* atkRelation =
-      atk_relation_set_get_relation_by_type(relation_set, atkType);
-    if (atkRelation)
-      atk_relation_set_remove(relation_set, atkRelation);
+#include "RelationTypeMap.h"
 
-    Relation rel(accWrap->RelationByType(relationTypes[i]));
-    nsTArray<AtkObject*> targets;
-    Accessible* tempAcc = nullptr;
-    while ((tempAcc = rel.Next()))
-      targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc));
-
-    if (targets.Length()) {
-      atkRelation = atk_relation_new(targets.Elements(), targets.Length(), atkType);
-      atk_relation_set_add(relation_set, atkRelation);
-      g_object_unref(atkRelation);
-    }
-  }
+#undef RELATIONTYPE
 
   return relation_set;
 }
 
 // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap
 // for it.
 AccessibleWrap*
 GetAccessibleWrap(AtkObject* aAtkObj)
rename from accessible/src/base/RoleAsserts.cpp
rename to accessible/src/base/Asserts.cpp
--- a/accessible/src/base/RoleAsserts.cpp
+++ b/accessible/src/base/Asserts.cpp
@@ -1,17 +1,26 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "nsIAccessibleRelation.h"
 #include "nsIAccessibleRole.h"
+#include "RelationType.h"
 #include "Role.h"
 
 using namespace mozilla::a11y;
 
 #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
   static_assert(static_cast<uint32_t>(roles::geckoRole) \
                 == static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \
                 "internal and xpcom roles differ!");
 #include "RoleMap.h"
 #undef ROLE
+
+#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
+  static_assert(static_cast<uint32_t>(RelationType::geckoType) \
+                == static_cast<uint32_t>(nsIAccessibleRelation::RELATION_ ## geckoType), \
+                "internal and xpcom relations differ!");
+#include "RelationTypeMap.h"
+#undef RELATIONTYPE
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/RelationTypeMap.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Usage: declare the macro RELATIONTYPE()with the following arguments:
+ * RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type)
+ */
+
+RELATIONTYPE(LABELLED_BY,
+             "labelled by",
+             ATK_RELATION_LABELLED_BY,
+             NAVRELATION_LABELLED_BY,
+             IA2_RELATION_LABELLED_BY)
+
+RELATIONTYPE(LABEL_FOR,
+             "label for",
+             ATK_RELATION_LABEL_FOR,
+             NAVRELATION_LABEL_FOR,
+             IA2_RELATION_LABEL_FOR)
+
+RELATIONTYPE(DESCRIBED_BY,
+             "described by",
+             ATK_RELATION_DESCRIBED_BY,
+             NAVRELATION_DESCRIBED_BY,
+             IA2_RELATION_DESCRIBED_BY)
+
+RELATIONTYPE(DESCRIPTION_FOR,
+             "description for",
+             ATK_RELATION_DESCRIPTION_FOR,
+             NAVRELATION_DESCRIPTION_FOR,
+             IA2_RELATION_DESCRIPTION_FOR)
+
+RELATIONTYPE(NODE_CHILD_OF,
+             "node child of",
+             ATK_RELATION_NODE_CHILD_OF,
+             NAVRELATION_NODE_CHILD_OF,
+             IA2_RELATION_NODE_CHILD_OF)
+
+RELATIONTYPE(NODE_PARENT_OF,
+             "node parent of",
+             ATK_RELATION_NODE_PARENT_OF,
+             NAVRELATION_NODE_PARENT_OF,
+             IA2_RELATION_NODE_PARENT_OF)
+
+RELATIONTYPE(CONTROLLED_BY,
+             "controlled by",
+             ATK_RELATION_CONTROLLED_BY,
+             NAVRELATION_CONTROLLED_BY,
+             IA2_RELATION_CONTROLLED_BY)
+
+RELATIONTYPE(CONTROLLER_FOR,
+             "controller for",
+             ATK_RELATION_CONTROLLER_FOR,
+             NAVRELATION_CONTROLLER_FOR,
+             IA2_RELATION_CONTROLLER_FOR)
+
+RELATIONTYPE(FLOWS_TO,
+             "flows to",
+             ATK_RELATION_FLOWS_TO,
+             NAVRELATION_FLOWS_TO,
+             IA2_RELATION_FLOWS_TO)
+
+RELATIONTYPE(FLOWS_FROM,
+             "flows from",
+             ATK_RELATION_FLOWS_FROM,
+             NAVRELATION_FLOWS_FROM,
+             IA2_RELATION_FLOWS_FROM)
+
+RELATIONTYPE(MEMBER_OF,
+             "member of",
+             ATK_RELATION_MEMBER_OF,
+             NAVRELATION_MEMBER_OF,
+             IA2_RELATION_MEMBER_OF)
+
+RELATIONTYPE(SUBWINDOW_OF,
+             "subwindow of",
+             ATK_RELATION_SUBWINDOW_OF,
+             NAVRELATION_SUBWINDOW_OF,
+             IA2_RELATION_SUBWINDOW_OF)
+
+RELATIONTYPE(EMBEDS,
+             "embeds",
+             ATK_RELATION_EMBEDS,
+             NAVRELATION_EMBEDS,
+             IA2_RELATION_EMBEDS)
+
+RELATIONTYPE(EMBEDDED_BY,
+             "embedded by",
+             ATK_RELATION_EMBEDDED_BY,
+             NAVRELATION_EMBEDDED_BY,
+             IA2_RELATION_EMBEDDED_BY)
+
+RELATIONTYPE(POPUP_FOR,
+             "popup for",
+             ATK_RELATION_POPUP_FOR,
+             NAVRELATION_POPUP_FOR,
+             IA2_RELATION_POPUP_FOR)
+
+RELATIONTYPE(PARENT_WINDOW_OF,
+             "parent window of",
+             ATK_RELATION_PARENT_WINDOW_OF,
+             NAVRELATION_PARENT_WINDOW_OF,
+             IA2_RELATION_PARENT_WINDOW_OF)
+
+RELATIONTYPE(DEFAULT_BUTTON,
+             "default button",
+             ATK_RELATION_NULL,
+             NAVRELATION_DEFAULT_BUTTON,
+             IA2_RELATION_NULL)
--- a/accessible/src/base/moz.build
+++ b/accessible/src/base/moz.build
@@ -32,29 +32,29 @@ if CONFIG['MOZ_DEBUG']:
 
 CPP_SOURCES += [
     'AccCollector.cpp',
     'AccEvent.cpp',
     'AccGroupInfo.cpp',
     'AccIterator.cpp',
     'ARIAMap.cpp',
     'ARIAStateMap.cpp',
+    'Asserts.cpp',
     'DocManager.cpp',
     'EventQueue.cpp',
     'Filters.cpp',
     'FocusManager.cpp',
     'NotificationController.cpp',
     'nsAccessibilityService.cpp',
     'nsAccessiblePivot.cpp',
     'nsAccessNode.cpp',
     'nsAccUtils.cpp',
     'nsCoreUtils.cpp',
     'nsEventShell.cpp',
     'nsTextEquivUtils.cpp',
-    'RoleAsserts.cpp',
     'SelectionManager.cpp',
     'StyleInfo.cpp',
     'TextAttrs.cpp',
     'TextUpdater.cpp',
     'TreeWalker.cpp',
 ]
 
 if a11y_log:
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -708,23 +708,32 @@ nsAccessibilityService::GetStringEventTy
   return NS_OK;
 }
 
 // nsIAccessibleRetrieval::getStringRelationType()
 NS_IMETHODIMP
 nsAccessibilityService::GetStringRelationType(uint32_t aRelationType,
                                               nsAString& aString)
 {
-  if (aRelationType >= ArrayLength(kRelationTypeNames)) {
-    aString.AssignLiteral("unknown");
+  NS_ENSURE_ARG(aRelationType <= static_cast<uint32_t>(RelationType::LAST));
+
+#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
+  case RelationType::geckoType: \
+    aString.AssignLiteral(geckoTypeName); \
     return NS_OK;
+
+  RelationType relationType = static_cast<RelationType>(aRelationType);
+  switch (relationType) {
+#include "RelationTypeMap.h"
+    default:
+      aString.AssignLiteral("unknown");
+      return NS_OK;
   }
 
-  CopyUTF8toUTF16(kRelationTypeNames[aRelationType], aString);
-  return NS_OK;
+#undef RELATIONTYPE
 }
 
 NS_IMETHODIMP
 nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
                                                nsIAccessible** aAccessible)
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
   *aAccessible = nullptr;
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -327,34 +327,10 @@ static const char kEventTypeNames[][40] 
   "hypertext link selected",                 // EVENT_HYPERTEXT_LINK_SELECTED
   "hyperlink start index changed",           // EVENT_HYPERLINK_START_INDEX_CHANGED
   "hypertext changed",                       // EVENT_HYPERTEXT_CHANGED
   "hypertext links count changed",           // EVENT_HYPERTEXT_NLINKS_CHANGED
   "object attribute changed",                // EVENT_OBJECT_ATTRIBUTE_CHANGED
   "virtual cursor changed"                   // EVENT_VIRTUALCURSOR_CHANGED
 };
 
-/**
- * Map nsIAccessibleRelation constants to strings. Used by
- * nsIAccessibleRetrieval::getStringRelationType() method.
- */
-static const char kRelationTypeNames[][20] = {
-  "labelled by",         // RELATION_LABELLED_BY
-  "label for",           // RELATION_LABEL_FOR
-  "described by",        // RELATION_DESCRIBED_BY
-  "description for",     // RELATION_DESCRIPTION_FOR
-  "node child of",       // RELATION_NODE_CHILD_OF
-  "node parent of",      // RELATION_NODE_PARENT_OF
-  "controlled by",       // RELATION_CONTROLLED_BY
-  "controller for",      // RELATION_CONTROLLER_FOR
-  "flows to",            // RELATION_FLOWS_TO
-  "flows from",          // RELATION_FLOWS_FROM
-  "member of",           // RELATION_MEMBER_OF
-  "subwindow of",        // RELATION_SUBWINDOW_OF
-  "embeds",              // RELATION_EMBEDS
-  "embedded by",         // RELATION_EMBEDDED_BY
-  "popup for",           // RELATION_POPUP_FOR
-  "parent window of",    // RELATION_PARENT_WINDOW_OF
-  "default button"       // RELATION_DEFAULT_BUTTON
-};
-
 #endif /* __nsIAccessibilityService_h__ */
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -25,16 +25,17 @@
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
 #include "TreeWalker.h"
 
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMHTMLElement.h"
+#include "nsIDOMKeyEvent.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMXULButtonElement.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMXULLabelElement.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsPIDOMWindow.h"
--- a/accessible/src/windows/ia2/ia2Accessible.cpp
+++ b/accessible/src/windows/ia2/ia2Accessible.cpp
@@ -55,18 +55,21 @@ ia2Accessible::get_nRelations(long* aNRe
   if (!aNRelations)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
-    Relation rel = acc->RelationByType(sRelationTypesForIA2[idx]);
+  for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
+    if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
+      continue;
+
+    Relation rel = acc->RelationByType(sRelationTypePairs[idx].first);
     if (rel.Next())
       (*aNRelations)++;
   }
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -80,18 +83,21 @@ ia2Accessible::get_relation(long aRelati
     return E_INVALIDARG;
   *aRelation = nullptr;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   long relIdx = 0;
-  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
-    RelationType relationType = sRelationTypesForIA2[idx];
+  for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
+    if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
+      continue;
+
+    RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
     nsRefPtr<ia2AccessibleRelation> ia2Relation =
       new ia2AccessibleRelation(relationType, &rel);
     if (ia2Relation->HasTargets()) {
       if (relIdx == aRelationIndex) {
         ia2Relation.forget(aRelation);
         return S_OK;
       }
@@ -115,19 +121,22 @@ ia2Accessible::get_relations(long aMaxRe
   if (!aRelation || !aNRelations)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2) &&
+  for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) &&
        *aNRelations < aMaxRelations; idx++) {
-    RelationType relationType = sRelationTypesForIA2[idx];
+    if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
+      continue;
+
+    RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
     nsRefPtr<ia2AccessibleRelation> ia2Rel =
       new ia2AccessibleRelation(relationType, &rel);
     if (ia2Rel->HasTargets()) {
       ia2Rel.forget(aRelation + (*aNRelations));
       (*aNRelations)++;
     }
   }
--- a/accessible/src/windows/ia2/ia2AccessibleRelation.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleRelation.cpp
@@ -28,74 +28,32 @@ ia2AccessibleRelation::ia2AccessibleRela
 IMPL_IUNKNOWN_QUERY_HEAD(ia2AccessibleRelation)
   IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleRelation)
   IMPL_IUNKNOWN_QUERY_IFACE(IUnknown)
 IMPL_IUNKNOWN_QUERY_TAIL
 
 // IAccessibleRelation
 
 STDMETHODIMP
-ia2AccessibleRelation::get_relationType(BSTR *aRelationType)
+ia2AccessibleRelation::get_relationType(BSTR* aRelationType)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aRelationType)
     return E_INVALIDARG;
 
   *aRelationType = nullptr;
 
+#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
+  case RelationType::geckoType: \
+    *aRelationType = ::SysAllocString(ia2Type); \
+    break;
+
   switch (mType) {
-    case RelationType::CONTROLLED_BY:
-      *aRelationType = ::SysAllocString(IA2_RELATION_CONTROLLED_BY);
-      break;
-    case RelationType::CONTROLLER_FOR:
-      *aRelationType = ::SysAllocString(IA2_RELATION_CONTROLLER_FOR);
-      break;
-    case RelationType::DESCRIBED_BY:
-      *aRelationType = ::SysAllocString(IA2_RELATION_DESCRIBED_BY);
-      break;
-    case RelationType::DESCRIPTION_FOR:
-      *aRelationType = ::SysAllocString(IA2_RELATION_DESCRIPTION_FOR);
-      break;
-    case RelationType::EMBEDDED_BY:
-      *aRelationType = ::SysAllocString(IA2_RELATION_EMBEDDED_BY);
-      break;
-    case RelationType::EMBEDS:
-      *aRelationType = ::SysAllocString(IA2_RELATION_EMBEDS);
-      break;
-    case RelationType::FLOWS_FROM:
-      *aRelationType = ::SysAllocString(IA2_RELATION_FLOWS_FROM);
-      break;
-    case RelationType::FLOWS_TO:
-      *aRelationType = ::SysAllocString(IA2_RELATION_FLOWS_TO);
-      break;
-    case RelationType::LABEL_FOR:
-      *aRelationType = ::SysAllocString(IA2_RELATION_LABEL_FOR);
-      break;
-    case RelationType::LABELLED_BY:
-      *aRelationType = ::SysAllocString(IA2_RELATION_LABELED_BY);
-      break;
-    case RelationType::MEMBER_OF:
-      *aRelationType = ::SysAllocString(IA2_RELATION_MEMBER_OF);
-      break;
-    case RelationType::NODE_CHILD_OF:
-      *aRelationType = ::SysAllocString(IA2_RELATION_NODE_CHILD_OF);
-      break;
-    case RelationType::NODE_PARENT_OF:
-      *aRelationType = ::SysAllocString(IA2_RELATION_NODE_PARENT_OF);
-      break;
-    case RelationType::PARENT_WINDOW_OF:
-      *aRelationType = ::SysAllocString(IA2_RELATION_PARENT_WINDOW_OF);
-      break;
-    case RelationType::POPUP_FOR:
-      *aRelationType = ::SysAllocString(IA2_RELATION_POPUP_FOR);
-      break;
-    case RelationType::SUBWINDOW_OF:
-      *aRelationType = ::SysAllocString(IA2_RELATION_SUBWINDOW_OF);
-      break;
+#include "RelationTypeMap.h"
   }
 
   return *aRelationType ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
@@ -161,9 +119,8 @@ ia2AccessibleRelation::get_targets(long 
   for (long idx = 0; idx < maxTargets; idx++)
     get_target(idx, aTargets + idx);
 
   *aNTargets = maxTargets;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
-
--- a/accessible/src/windows/ia2/ia2AccessibleRelation.h
+++ b/accessible/src/windows/ia2/ia2AccessibleRelation.h
@@ -7,16 +7,17 @@
 
 #ifndef _NS_ACCESSIBLE_RELATION_WRAP_H
 #define _NS_ACCESSIBLE_RELATION_WRAP_H
 
 #include "Accessible.h"
 #include "IUnknownImpl.h"
 #include "nsIAccessibleRelation.h"
 
+#include <utility>
 #include "nsTArray.h"
 
 #include "AccessibleRelation.h"
 
 namespace mozilla {
 namespace a11y {
 
 class ia2AccessibleRelation MOZ_FINAL : public IAccessibleRelation
@@ -55,34 +56,27 @@ private:
   ia2AccessibleRelation& operator = (const ia2AccessibleRelation&);
 
   RelationType mType;
   nsTArray<nsRefPtr<Accessible> > mTargets;
 };
 
 
 /**
- * Relations exposed to IAccessible2.
+ * Gecko to IAccessible2 relation types map.
  */
-static const RelationType sRelationTypesForIA2[] = {
-  RelationType::LABELLED_BY,
-  RelationType::LABEL_FOR,
-  RelationType::DESCRIBED_BY,
-  RelationType::DESCRIPTION_FOR,
-  RelationType::NODE_CHILD_OF,
-  RelationType::NODE_PARENT_OF,
-  RelationType::CONTROLLED_BY,
-  RelationType::CONTROLLER_FOR,
-  RelationType::FLOWS_TO,
-  RelationType::FLOWS_FROM,
-  RelationType::MEMBER_OF,
-  RelationType::SUBWINDOW_OF,
-  RelationType::EMBEDS,
-  RelationType::EMBEDDED_BY,
-  RelationType::POPUP_FOR,
-  RelationType::PARENT_WINDOW_OF
+
+const WCHAR *const IA2_RELATION_NULL = L"";
+
+#define RELATIONTYPE(geckoType, name, atkType, msaaType, ia2Type) \
+  std::pair<RelationType, const WCHAR *const>(RelationType::geckoType, ia2Type),
+
+static const std::pair<RelationType, const WCHAR *const> sRelationTypePairs[] = {
+#include "RelationTypeMap.h"
 };
 
+#undef RELATIONTYPE
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -893,16 +893,21 @@ AccessibleWrap::accNavigate(
     return E_INVALIDARG;
 
   if (accessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   Accessible* navAccessible = nullptr;
   Maybe<RelationType> xpRelation;
 
+#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
+  case msaaType: \
+    xpRelation.construct(RelationType::geckoType); \
+    break;
+
   switch(navDir) {
     case NAVDIR_FIRSTCHILD:
       if (!nsAccUtils::MustPrune(accessible))
         navAccessible = accessible->FirstChild();
       break;
     case NAVDIR_LASTCHILD:
       if (!nsAccUtils::MustPrune(accessible))
         navAccessible = accessible->LastChild();
@@ -915,72 +920,24 @@ AccessibleWrap::accNavigate(
       break;
     case NAVDIR_DOWN:
     case NAVDIR_LEFT:
     case NAVDIR_RIGHT:
     case NAVDIR_UP:
       return E_NOTIMPL;
 
     // MSAA relationship extensions to accNavigate
-    case NAVRELATION_CONTROLLED_BY:
-      xpRelation.construct(RelationType::CONTROLLED_BY);
-      break;
-    case NAVRELATION_CONTROLLER_FOR:
-      xpRelation.construct(RelationType::CONTROLLER_FOR);
-      break;
-    case NAVRELATION_LABEL_FOR:
-      xpRelation.construct(RelationType::LABEL_FOR);
-      break;
-    case NAVRELATION_LABELLED_BY:
-      xpRelation.construct(RelationType::LABELLED_BY);
-      break;
-    case NAVRELATION_MEMBER_OF:
-      xpRelation.construct(RelationType::MEMBER_OF);
-      break;
-    case NAVRELATION_NODE_CHILD_OF:
-      xpRelation.construct(RelationType::NODE_CHILD_OF);
-      break;
-    case NAVRELATION_FLOWS_TO:
-      xpRelation.construct(RelationType::FLOWS_TO);
-      break;
-    case NAVRELATION_FLOWS_FROM:
-      xpRelation.construct(RelationType::FLOWS_FROM);
-      break;
-    case NAVRELATION_SUBWINDOW_OF:
-      xpRelation.construct(RelationType::SUBWINDOW_OF);
-      break;
-    case NAVRELATION_EMBEDS:
-      xpRelation.construct(RelationType::EMBEDS);
-      break;
-    case NAVRELATION_EMBEDDED_BY:
-      xpRelation.construct(RelationType::EMBEDDED_BY);
-      break;
-    case NAVRELATION_POPUP_FOR:
-      xpRelation.construct(RelationType::POPUP_FOR);
-      break;
-    case NAVRELATION_PARENT_WINDOW_OF:
-      xpRelation.construct(RelationType::PARENT_WINDOW_OF);
-      break;
-    case NAVRELATION_DEFAULT_BUTTON:
-      xpRelation.construct(RelationType::DEFAULT_BUTTON);
-      break;
-    case NAVRELATION_DESCRIBED_BY:
-      xpRelation.construct(RelationType::DESCRIBED_BY);
-      break;
-    case NAVRELATION_DESCRIPTION_FOR:
-      xpRelation.construct(RelationType::DESCRIPTION_FOR);
-      break;
-    case NAVRELATION_NODE_PARENT_OF:
-      xpRelation.construct(RelationType::NODE_PARENT_OF);
-      break;
+#include "RelationTypeMap.h"
 
     default:
       return E_INVALIDARG;
   }
 
+#undef RELATIONTYPE
+
   pvarEndUpAt->vt = VT_EMPTY;
 
   if (!xpRelation.empty()) {
     Relation rel = RelationByType(xpRelation.ref());
     navAccessible = rel.Next();
   }
 
   if (!navAccessible)
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -589,17 +589,17 @@ pref("browser.gesture.pinch.in.shift", "
 #endif
 pref("browser.gesture.twist.latched", false);
 pref("browser.gesture.twist.threshold", 0);
 pref("browser.gesture.twist.right", "cmd_gestureRotateRight");
 pref("browser.gesture.twist.left", "cmd_gestureRotateLeft");
 pref("browser.gesture.twist.end", "cmd_gestureRotateEnd");
 pref("browser.gesture.tap", "cmd_fullZoomReset");
 
-pref("browser.snapshots.limit", 0);
+pref("browser.snapshots.limit", 5);
 
 // 0: Nothing happens
 // 1: Scrolling contents
 // 2: Go back or go forward, in your history
 // 3: Zoom in or out.
 #ifdef XP_MACOSX
 // On OS X, if the wheel has one axis only, shift+wheel comes through as a
 // horizontal scroll event. Thus, we can't assign anything other than normal
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -73,25 +73,28 @@ const EVENTS = {
 
   // When the widgets layout has been changed.
   LAYOUT_CHANGED: "Debugger:LayoutChanged"
 };
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
 Cu.import("resource:///modules/devtools/shared/event-emitter.js");
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
+const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const Editor = require("devtools/sourceeditor/editor");
+const promise = require("sdk/core/promise");
+const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
+
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "devtools",
   "resource://gre/modules/devtools/Loader.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
   "resource://gre/modules/devtools/DevToolsUtils.jsm");
@@ -603,17 +606,17 @@ StackFrames.prototype = {
     this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
     DebuggerView.editor.focus();
   },
 
   /**
    * Handler for the thread client's resumed notification.
    */
   _onResumed: function() {
-    DebuggerView.editor.setDebugLocation(-1);
+    DebuggerView.editor.clearDebugLocation();
 
     // Prepare the watch expression evaluation string for the next pause.
     if (!this._isWatchExpressionsEvaluation) {
       this._currentWatchExpressions = this._syncedWatchExpressions;
     }
   },
 
   /**
@@ -1429,17 +1432,16 @@ EventListeners.prototype = {
     DebuggerView.EventListeners.commit();
   }
 };
 
 /**
  * Handles all the breakpoints in the current debugger.
  */
 function Breakpoints() {
-  this._onEditorBreakpointChange = this._onEditorBreakpointChange.bind(this);
   this._onEditorBreakpointAdd = this._onEditorBreakpointAdd.bind(this);
   this._onEditorBreakpointRemove = this._onEditorBreakpointRemove.bind(this);
   this.addBreakpoint = this.addBreakpoint.bind(this);
   this.removeBreakpoint = this.removeBreakpoint.bind(this);
 }
 
 Breakpoints.prototype = {
   /**
@@ -1452,90 +1454,76 @@ Breakpoints.prototype = {
 
   /**
    * Adds the source editor breakpoint handlers.
    *
    * @return object
    *         A promise that is resolved when the breakpoints finishes initializing.
    */
   initialize: function() {
-    DebuggerView.editor.addEventListener(
-      SourceEditor.EVENTS.BREAKPOINT_CHANGE, this._onEditorBreakpointChange);
+    DebuggerView.editor.on("breakpointAdded", this._onEditorBreakpointAdd);
+    DebuggerView.editor.on("breakpointRemoved", this._onEditorBreakpointRemove);
 
     // Initialization is synchronous, for now.
     return promise.resolve(null);
   },
 
   /**
    * Removes the source editor breakpoint handlers & all the added breakpoints.
    *
    * @return object
    *         A promise that is resolved when the breakpoints finishes destroying.
    */
   destroy: function() {
-    DebuggerView.editor.removeEventListener(
-      SourceEditor.EVENTS.BREAKPOINT_CHANGE, this._onEditorBreakpointChange);
+    DebuggerView.editor.off("breakpointAdded", this._onEditorBreakpointAdd);
+    DebuggerView.editor.off("breakpointRemoved", this._onEditorBreakpointRemove);
 
     return this.removeAllBreakpoints();
   },
 
   /**
-   * Event handler for breakpoint changes that happen in the editor. This
-   * function syncs the breakpoints in the editor to those in the debugger.
-   *
-   * @param object aEvent
-   *        The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object.
-   */
-  _onEditorBreakpointChange: function(aEvent) {
-    aEvent.added.forEach(this._onEditorBreakpointAdd, this);
-    aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
-  },
-
-  /**
    * Event handler for new breakpoints that come from the editor.
    *
-   * @param object aEditorBreakpoint
-   *        The breakpoint object coming from the editor.
+   * @param number aLine
+   *        Line number where breakpoint was set.
    */
-  _onEditorBreakpointAdd: function(aEditorBreakpoint) {
+  _onEditorBreakpointAdd: function(_, aLine) {
     let url = DebuggerView.Sources.selectedValue;
-    let line = aEditorBreakpoint.line + 1;
-    let location = { url: url, line: line };
+    let location = { url: url, line: aLine + 1 };
 
     // Initialize the breakpoint, but don't update the editor, since this
     // callback is invoked because a breakpoint was added in the editor itself.
     this.addBreakpoint(location, { noEditorUpdate: true }).then(aBreakpointClient => {
       // If the breakpoint client has an "requestedLocation" attached, then
       // the original requested placement for the breakpoint wasn't accepted.
       // In this case, we need to update the editor with the new location.
       if (aBreakpointClient.requestedLocation) {
         DebuggerView.editor.removeBreakpoint(aBreakpointClient.requestedLocation.line - 1);
         DebuggerView.editor.addBreakpoint(aBreakpointClient.location.line - 1);
       }
       // Notify that we've shown a breakpoint in the source editor.
-      window.emit(EVENTS.BREAKPOINT_SHOWN, aEditorBreakpoint);
+      window.emit(EVENTS.BREAKPOINT_SHOWN);
     });
   },
 
   /**
    * Event handler for breakpoints that are removed from the editor.
    *
-   * @param object aEditorBreakpoint
-   *        The breakpoint object that was removed from the editor.
+   * @param number aLine
+   *        Line number where breakpoint was removed.
    */
-  _onEditorBreakpointRemove: function(aEditorBreakpoint) {
+  _onEditorBreakpointRemove: function(_, aLine) {
     let url = DebuggerView.Sources.selectedValue;
-    let line = aEditorBreakpoint.line + 1;
-    let location = { url: url, line: line };
+    let location = { url: url, line: aLine + 1 };
 
     // Destroy the breakpoint, but don't update the editor, since this callback
     // is invoked because a breakpoint was removed from the editor itself.
     this.removeBreakpoint(location, { noEditorUpdate: true }).then(() => {
       // Notify that we've hidden a breakpoint in the source editor.
-      window.emit(EVENTS.BREAKPOINT_HIDDEN, aEditorBreakpoint);
+      window.emit(EVENTS.BREAKPOINT_HIDDEN);
     });
   },
 
   /**
    * Update the breakpoints in the editor view. This function takes the list of
    * breakpoints in the debugger and adds them back into the editor view.
    * This is invoked when the selected script is changed, or when new sources
    * are received via the _onNewSource and _onSourcesAdded event listeners.
@@ -1639,17 +1627,17 @@ Breakpoints.prototype = {
       // so that they may not be forgotten across target navigations.
       this._disabled.delete(identifier);
 
       // Preserve information about the breakpoint's line text, to display it
       // in the sources pane without requiring fetching the source (for example,
       // after the target navigated). Note that this will get out of sync
       // if the source text contents change.
       let line = aBreakpointClient.location.line - 1;
-      aBreakpointClient.text = DebuggerView.getEditorLineText(line).trim();
+      aBreakpointClient.text = DebuggerView.editor.getText(line).trim();
 
       // Show the breakpoint in the editor and breakpoints pane, and resolve.
       this._showBreakpoint(aBreakpointClient, aOptions);
 
       // Notify that we've added a breakpoint.
       window.emit(EVENTS.BREAKPOINT_ADDED, aBreakpointClient);
       deferred.resolve(aBreakpointClient);
     });
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -9,17 +9,17 @@
  * Functions handling the sources UI.
  */
 function SourcesView() {
   dumpn("SourcesView was instantiated");
 
   this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
   this._onEditorLoad = this._onEditorLoad.bind(this);
   this._onEditorUnload = this._onEditorUnload.bind(this);
-  this._onEditorSelection = this._onEditorSelection.bind(this);
+  this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this);
   this._onEditorContextMenu = this._onEditorContextMenu.bind(this);
   this._onSourceSelect = this._onSourceSelect.bind(this);
   this._onSourceClick = this._onSourceClick.bind(this);
   this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
   this._onSourceCheck = this._onSourceCheck.bind(this);
   this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
   this._onBreakpointClick = this._onBreakpointClick.bind(this);
   this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this);
@@ -474,17 +474,22 @@ SourcesView.prototype = Heritage.extend(
     }
   },
 
   /**
    * Hides a conditional breakpoint's expression input popup.
    */
   _hideConditionalPopup: function() {
     this._cbPanel.hidden = true;
-    this._cbPanel.hidePopup();
+
+    // Sometimes this._cbPanel doesn't have hidePopup method which doesn't
+    // break anything but simply outputs an exception to the console.
+    if (this._cbPanel.hidePopup) {
+      this._cbPanel.hidePopup();
+    }
   },
 
   /**
    * Customization function for creating a breakpoint item's UI.
    *
    * @param object aOptions
    *        A couple of options or flags supported by this operation:
    *          - location: the breakpoint's source location and line number
@@ -640,53 +645,51 @@ SourcesView.prototype = Heritage.extend(
       this._selectedBreakpointItem = null;
     }
   },
 
   /**
    * The load listener for the source editor.
    */
   _onEditorLoad: function(aName, aEditor) {
-    aEditor.addEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false);
-    aEditor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false);
+    aEditor.on("cursorActivity", this._onEditorCursorActivity);
+    aEditor.on("contextMenu", this._onEditorContextMenu);
   },
 
   /**
    * The unload listener for the source editor.
    */
   _onEditorUnload: function(aName, aEditor) {
-    aEditor.removeEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false);
-    aEditor.removeEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false);
+    aEditor.off("cursorActivity", this._onEditorCursorActivity);
+    aEditor.off("contextMenu", this._onEditorContextMenu);
   },
 
   /**
    * The selection listener for the source editor.
    */
-  _onEditorSelection: function(e) {
-    let { start, end } = e.newValue;
+  _onEditorCursorActivity: function(e) {
+    let editor = DebuggerView.editor;
+    let start  = editor.getCursor("start").line + 1;
+    let end    = editor.getCursor().line + 1;
+    let url    = this.selectedValue;
 
-    let url = this.selectedValue;
-    let lineStart = DebuggerView.editor.getLineAtOffset(start) + 1;
-    let lineEnd = DebuggerView.editor.getLineAtOffset(end) + 1;
-    let location = { url: url, line: lineStart };
+    let location = { url: url, line: start };
 
-    if (this.getBreakpoint(location) && lineStart == lineEnd) {
+    if (this.getBreakpoint(location) && start == end) {
       this.highlightBreakpoint(location, { noEditorUpdate: true });
     } else {
       this.unhighlightBreakpoint();
     }
   },
 
   /**
    * The context menu listener for the source editor.
    */
   _onEditorContextMenu: function({ x, y }) {
-    let offset = DebuggerView.editor.getOffsetAtLocation(x, y);
-    let line = DebuggerView.editor.getLineAtOffset(offset);
-    this._editorContextMenuLineNumber = line;
+    this._editorContextMenuLineNumber = DebuggerView.editor.getPositionFromCoords(x, y).line;
   },
 
   /**
    * The select listener for the sources container.
    */
   _onSourceSelect: function({ detail: sourceItem }) {
     if (!sourceItem) {
       return;
@@ -864,23 +867,24 @@ SourcesView.prototype = Heritage.extend(
 
   /**
    * Called when the add breakpoint key sequence was pressed.
    */
   _onCmdAddBreakpoint: function() {
     // If this command was executed via the context menu, add the breakpoint
     // on the currently hovered line in the source editor.
     if (this._editorContextMenuLineNumber >= 0) {
-      DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber);
+      DebuggerView.editor.setCursor({ line: this._editorContextMenuLineNumber, ch: 0 });
     }
+
     // Avoid placing breakpoints incorrectly when using key shortcuts.
     this._editorContextMenuLineNumber = -1;
 
     let url = DebuggerView.Sources.selectedValue;
-    let line = DebuggerView.editor.getCaretPosition().line + 1;
+    let line = DebuggerView.editor.getCursor().line + 1;
     let location = { url: url, line: line };
     let breakpointItem = this.getBreakpoint(location);
 
     // If a breakpoint already existed, remove it now.
     if (breakpointItem) {
       DebuggerController.Breakpoints.removeBreakpoint(location);
     }
     // No breakpoint existed at the required location, add one now.
@@ -891,23 +895,24 @@ SourcesView.prototype = Heritage.extend(
 
   /**
    * Called when the add conditional breakpoint key sequence was pressed.
    */
   _onCmdAddConditionalBreakpoint: function() {
     // If this command was executed via the context menu, add the breakpoint
     // on the currently hovered line in the source editor.
     if (this._editorContextMenuLineNumber >= 0) {
-      DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber);
+      DebuggerView.editor.setCursor({ line: this._editorContextMenuLineNumber, ch: 0 });
     }
+
     // Avoid placing breakpoints incorrectly when using key shortcuts.
     this._editorContextMenuLineNumber = -1;
 
     let url =  DebuggerView.Sources.selectedValue;
-    let line = DebuggerView.editor.getCaretPosition().line + 1;
+    let line = DebuggerView.editor.getCursor().line + 1;
     let location = { url: url, line: line };
     let breakpointItem = this.getBreakpoint(location);
 
     // If a breakpoint already existed or wasn't a conditional, morph it now.
     if (breakpointItem) {
       this.highlightBreakpoint(location, { openPopup: true });
     }
     // No breakpoint existed at the required location, add one now.
@@ -1451,17 +1456,17 @@ WatchExpressionsView.prototype = Heritag
   },
 
   /**
    * Called when the add watch expression key sequence was pressed.
    */
   _onCmdAddExpression: function(aText) {
     // Only add a new expression if there's no pending input.
     if (this.getAllStrings().indexOf("") == -1) {
-      this.addExpression(aText || DebuggerView.editor.getSelectedText());
+      this.addExpression(aText || DebuggerView.editor.getSelection());
     }
   },
 
   /**
    * Called when the remove all watch expressions key sequence was pressed.
    */
   _onCmdRemoveAllExpressions: function() {
     // Empty the view of all the watch expressions and clear the cache.
@@ -2095,22 +2100,19 @@ GlobalSearchView.prototype = Heritage.ex
 
     sourceResultsItem.instance.expand();
     this._currentlyFocusedMatch = LineResults.indexOfElement(target);
     this._scrollMatchIntoViewIfNeeded(target);
     this._bounceMatch(target);
 
     let url = sourceResultsItem.instance.url;
     let line = lineResultsItem.instance.line;
+
     DebuggerView.setEditorLocation(url, line + 1, { noDebug: true });
-
-    let editor = DebuggerView.editor;
-    let offset = editor.getCaretOffset();
-    let { start, length } = lineResultsItem.lineData.range;
-    editor.setSelection(offset + start, offset + start + length);
+    DebuggerView.editor.extendSelection(lineResultsItem.lineData.range);
   },
 
   /**
    * Scrolls a match into view if not already visible.
    *
    * @param nsIDOMNode aMatch
    *        The match to scroll into view.
    */
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -856,38 +856,35 @@ FilterView.prototype = {
    * Performs a line search if necessary.
    * (Jump to lines in the currently visible source).
    *
    * @param number aLine
    *        The source line number to jump to.
    */
   _performLineSearch: function(aLine) {
     // Make sure we're actually searching for a valid line.
-    if (!aLine) {
-      return;
+    if (aLine) {
+      DebuggerView.editor.setCursor({ line: aLine - 1, ch: 0 });
     }
-    DebuggerView.editor.setCaretPosition(aLine - 1);
   },
 
   /**
    * Performs a token search if necessary.
    * (Search for tokens in the currently visible source).
    *
    * @param string aToken
    *        The source token to find.
    */
   _performTokenSearch: function(aToken) {
     // Make sure we're actually searching for a valid token.
     if (!aToken) {
       return;
     }
-    let offset = DebuggerView.editor.find(aToken, { ignoreCase: true });
-    if (offset > -1) {
-      DebuggerView.editor.setSelection(offset, offset + aToken.length)
-    }
+
+    DebuggerView.editor.find(aToken);
   },
 
   /**
    * The click listener for the search container.
    */
   _onClick: function() {
     // If there's some text in the searchbox, displaying a panel would
     // interfere with double/triple click default behaviors.
@@ -1038,35 +1035,29 @@ FilterView.prototype = {
       } else {
         this.clearSearch();
       }
       return;
     }
 
     // Jump to the next/previous instance of the currently searched token.
     if (isTokenSearch) {
-      let [, token] = args;
-      let methods = { selectNext: "findNext", selectPrev: "findPrevious" };
-
-      // Search for the token and select it.
-      let offset = DebuggerView.editor[methods[actionToPerform]](true);
-      if (offset > -1) {
-        DebuggerView.editor.setSelection(offset, offset + token.length)
-      }
+      let methods = { selectNext: "findNext", selectPrev: "findPrev" };
+      DebuggerView.editor[methods[actionToPerform]]();
       return;
     }
 
     // Increment/decrement the currently searched caret line.
     if (isLineSearch) {
       let [, line] = args;
       let amounts = { selectNext: 1, selectPrev: -1 };
 
       // Modify the line number and jump to it.
       line += !isReturnKey ? amounts[actionToPerform] : 0;
-      let lineCount = DebuggerView.editor.getLineCount();
+      let lineCount = DebuggerView.editor.lineCount();
       let lineTarget = line < 1 ? 1 : line > lineCount ? lineCount : line;
       this._doSearch(SEARCH_LINE_FLAG, lineTarget);
       return;
     }
   },
 
   /**
    * The blur listener for the search container.
@@ -1079,17 +1070,17 @@ FilterView.prototype = {
    * Called when a filtering key sequence was pressed.
    *
    * @param string aOperator
    *        The operator to use for filtering.
    */
   _doSearch: function(aOperator = "", aText = "") {
     this._searchbox.focus();
     this._searchbox.value = ""; // Need to clear value beforehand. Bug 779738.
-    this._searchbox.value = aOperator + (aText || DebuggerView.editor.getSelectedText());
+    this._searchbox.value = aOperator + (aText || DebuggerView.editor.getSelection());
   },
 
   /**
    * Called when the source location filter key sequence was pressed.
    */
   _doFileSearch: function() {
     this._doSearch();
     this._searchboxHelpPanel.openPopup(this._searchbox);
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -23,23 +23,16 @@ const GLOBAL_SEARCH_EXPAND_MAX_RESULTS =
 const GLOBAL_SEARCH_LINE_MAX_LENGTH = 300; // chars
 const GLOBAL_SEARCH_ACTION_MAX_DELAY = 1500; // ms
 const FUNCTION_SEARCH_ACTION_MAX_DELAY = 400; // ms
 const SEARCH_GLOBAL_FLAG = "!";
 const SEARCH_FUNCTION_FLAG = "@";
 const SEARCH_TOKEN_FLAG = "#";
 const SEARCH_LINE_FLAG = ":";
 const SEARCH_VARIABLE_FLAG = "*";
-const DEFAULT_EDITOR_CONFIG = {
-  mode: SourceEditor.MODES.TEXT,
-  readOnly: true,
-  showLineNumbers: true,
-  showAnnotationRuler: true,
-  showOverviewRuler: true
-};
 
 /**
  * Object defining the debugger view components.
  */
 let DebuggerView = {
   /**
    * Initializes the debugger view.
    *
@@ -182,29 +175,53 @@ let DebuggerView = {
         case "properties":
           window.emit(EVENTS.FETCHED_PROPERTIES);
           break;
       }
     });
   },
 
   /**
-   * Initializes the SourceEditor instance.
+   * Initializes the Editor instance.
    *
    * @param function aCallback
    *        Called after the editor finishes initializing.
    */
   _initializeEditor: function(aCallback) {
     dumpn("Initializing the DebuggerView editor");
 
-    this.editor = new SourceEditor();
-    this.editor.init(document.getElementById("editor"), DEFAULT_EDITOR_CONFIG, () => {
+    // This needs to be more localizable: see bug 929234.
+    let extraKeys = {};
+    extraKeys[(Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + "F"] = (cm) => {
+      DebuggerView.Filtering._doTokenSearch();
+    };
+
+    this.editor = new Editor({
+      mode: Editor.modes.text,
+      readOnly: true,
+      lineNumbers: true,
+      showAnnotationRuler: true,
+      gutters: [ "breakpoints" ],
+      extraKeys: extraKeys,
+      contextMenu: "sourceEditorContextMenu"
+    });
+
+    this.editor.appendTo(document.getElementById("editor")).then(() => {
+      this.editor.extend(DebuggerEditor);
       this._loadingText = L10N.getStr("loadingText");
       this._onEditorLoad(aCallback);
     });
+
+    this.editor.on("gutterClick", (ev, line) => {
+      if (this.editor.hasBreakpoint(line)) {
+        this.editor.removeBreakpoint(line);
+      } else {
+        this.editor.addBreakpoint(line);
+      }
+    });
   },
 
   /**
    * The load event handler for the source editor, also executing any necessary
    * post-load operations.
    *
    * @param function aCallback
    *        Called after the editor finishes loading.
@@ -214,17 +231,17 @@ let DebuggerView = {
 
     DebuggerController.Breakpoints.initialize().then(() => {
       window.emit(EVENTS.EDITOR_LOADED, this.editor);
       aCallback();
     });
   },
 
   /**
-   * Destroys the SourceEditor instance and also executes any necessary
+   * Destroys the Editor instance and also executes any necessary
    * post-unload operations.
    *
    * @param function aCallback
    *        Called after the editor finishes destroying.
    */
   _destroyEditor: function(aCallback) {
     dumpn("Destroying the DebuggerView editor");
 
@@ -271,50 +288,52 @@ let DebuggerView = {
   /**
    * Sets the currently displayed text contents in the source editor.
    * This resets the mode and undo stack.
    *
    * @param string aTextContent
    *        The source text content.
    */
   _setEditorText: function(aTextContent = "") {
-    this.editor.setMode(SourceEditor.MODES.TEXT);
+    this.editor.setMode(Editor.modes.text);
     this.editor.setText(aTextContent);
-    this.editor.resetUndo();
+    this.editor.clearHistory();
   },
 
   /**
    * Sets the proper editor mode (JS or HTML) according to the specified
    * content type, or by determining the type from the url or text content.
    *
    * @param string aUrl
    *        The source url.
    * @param string aContentType [optional]
    *        The source content type.
    * @param string aTextContent [optional]
    *        The source text content.
    */
   _setEditorMode: function(aUrl, aContentType = "", aTextContent = "") {
     // Avoid setting the editor mode for very large files.
+    // Is this still necessary? See bug 929225.
     if (aTextContent.length >= SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
-      this.editor.setMode(SourceEditor.MODES.TEXT);
+      return void this.editor.setMode(Editor.modes.text);
     }
+
     // Use JS mode for files with .js and .jsm extensions.
-    else if (SourceUtils.isJavaScript(aUrl, aContentType)) {
-      this.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+    if (SourceUtils.isJavaScript(aUrl, aContentType)) {
+      return void this.editor.setMode(Editor.modes.js);
     }
+
     // Use HTML mode for files in which the first non whitespace character is
     // &lt;, regardless of extension.
-    else if (aTextContent.match(/^\s*</)) {
-      this.editor.setMode(SourceEditor.MODES.HTML);
+    if (aTextContent.match(/^\s*</)) {
+      return void this.editor.setMode(Editor.modes.html);
     }
-    // Unknown languange, use plain text.
-    else {
-      this.editor.setMode(SourceEditor.MODES.TEXT);
-    }
+
+    // Unknown language, use text.
+    this.editor.setMode(Editor.modes.text);
   },
 
   /**
    * Sets the currently displayed source text in the editor.
    *
    * You should use DebuggerView.updateEditor instead. It updates the current
    * caret and debug location based on a requested url and line.
    *
@@ -410,53 +429,46 @@ let DebuggerView = {
       if (frame && frame.where.url == aUrl) {
         aLine = frame.where.line;
       }
     }
 
     let sourceItem = this.Sources.getItemByValue(aUrl);
     let sourceForm = sourceItem.attachment.source;
 
+    // Once we change the editor location, it replaces editor's contents.
+    // This means that the debug location information is now obsolete, so
+    // we need to clear it. We set a new location below, in this function.
+    this.editor.clearDebugLocation();
+
     // Make sure the requested source client is shown in the editor, then
     // update the source editor's caret position and debug location.
     return this._setEditorSource(sourceForm, aFlags).then(() => {
       // Line numbers in the source editor should start from 1. If invalid
       // or not specified, then don't do anything.
       if (aLine < 1) {
         return;
       }
+
       if (aFlags.charOffset) {
-        aLine += this.editor.getLineAtOffset(aFlags.charOffset);
+        aLine += this.editor.getPosition(aFlags.charOffset).line;
       }
+
       if (aFlags.lineOffset) {
         aLine += aFlags.lineOffset;
       }
+
       if (!aFlags.noCaret) {
-        this.editor.setCaretPosition(aLine - 1, aFlags.columnOffset);
+        this.editor.setCursor({ line: aLine -1, ch: aFlags.columnOffset || 0 });
       }
-      if (!aFlags.noDebug) {
-        this.editor.setDebugLocation(aLine - 1, aFlags.columnOffset);
-      }
-    });
-  },
 
-  /**
-   * Gets the text in the source editor's specified line.
-   *
-   * @param number aLine [optional]
-   *        The line to get the text from. If unspecified, it defaults to
-   *        the current caret position.
-   * @return string
-   *         The specified line's text.
-   */
-  getEditorLineText: function(aLine) {
-    let line = aLine || this.editor.getCaretPosition().line;
-    let start = this.editor.getLineStart(line);
-    let end = this.editor.getLineEnd(line);
-    return this.editor.getText(start, end);
+      if (!aFlags.noDebug) {
+        this.editor.setDebugLocation(aLine - 1);
+      }
+    }).then(null, console.error);
   },
 
   /**
    * Gets the visibility state of the instruments pane.
    * @return boolean
    */
   get instrumentsPaneHidden()
     this._instrumentsPane.hasAttribute("pane-collapsed"),
@@ -594,19 +606,19 @@ let DebuggerView = {
     this.GlobalSearch.clearView();
     this.ChromeGlobals.empty();
     this.StackFrames.empty();
     this.Sources.empty();
     this.Variables.empty();
     this.EventListeners.empty();
 
     if (this.editor) {
-      this.editor.setMode(SourceEditor.MODES.TEXT);
+      this.editor.setMode(Editor.modes.text);
       this.editor.setText("");
-      this.editor.resetUndo();
+      this.editor.clearHistory();
       this._editorSource = {};
     }
   },
 
   _startup: null,
   _shutdown: null,
   Toolbar: null,
   Options: null,
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -8,33 +8,31 @@
 <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/debugger.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/debugger.dtd">
   %debuggerDTD;
 ]>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/devtools/source-editor-overlay.xul"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         macanimationtype="document"
         fullscreenbutton="true"
         screenX="4" screenY="4"
         width="960" height="480"
         persist="screenX screenY width height sizemode">
 
   <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="text/javascript" src="debugger-controller.js"/>
   <script type="text/javascript" src="debugger-view.js"/>
   <script type="text/javascript" src="debugger-toolbar.js"/>
   <script type="text/javascript" src="debugger-panes.js"/>
 
   <commandset id="editMenuCommands"/>
-  <commandset id="sourceEditorCommands"/>
 
   <commandset id="debuggerCommands">
     <command id="prettyPrintCommand"
              oncommand="DebuggerView.Sources.togglePrettyPrint()"/>
     <command id="unBlackBoxButton"
              oncommand="DebuggerView.Sources._onStopBlackBoxing()"/>
     <command id="nextSourceCommand"
              oncommand="DebuggerView.Sources.selectNextItem()"/>
@@ -81,33 +79,33 @@
     <command id="toggleShowVariablesFilterBox"
              oncommand="DebuggerView.Options._toggleShowVariablesFilterBox()"/>
     <command id="toggleShowOriginalSource"
              oncommand="DebuggerView.Options._toggleShowOriginalSource()"/>
   </commandset>
 
   <popupset id="debuggerPopupset">
     <menupopup id="sourceEditorContextMenu"
-               onpopupshowing="goUpdateSourceEditorMenuItems()">
+               onpopupshowing="goUpdateGlobalEditMenuItems()">
       <menuitem id="se-dbg-cMenu-addBreakpoint"
                 label="&debuggerUI.seMenuBreak;"
                 key="addBreakpointKey"
                 command="addBreakpointCommand"/>
       <menuitem id="se-dbg-cMenu-addConditionalBreakpoint"
                 label="&debuggerUI.seMenuCondBreak;"
                 key="addConditionalBreakpointKey"
                 command="addConditionalBreakpointCommand"/>
       <menuitem id="se-dbg-cMenu-addAsWatch"
                 label="&debuggerUI.seMenuAddWatch;"
                 key="addWatchExpressionKey"
                 command="addWatchExpressionCommand"/>
       <menuseparator/>
-      <menuitem id="se-cMenu-copy"/>
+      <menuitem id="cMenu_copy"/>
       <menuseparator/>
-      <menuitem id="se-cMenu-selectAll"/>
+      <menuitem id="cMenu_selectAll"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findFile"
                 label="&debuggerUI.searchFile;"
                 accesskey="&debuggerUI.searchFile.key;"
                 key="fileSearchKey"
                 command="fileSearchCommand"/>
       <menuitem id="se-dbg-cMenu-findGlobal"
                 label="&debuggerUI.searchGlobal;"
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js
@@ -30,17 +30,17 @@ function test() {
   function performTest() {
     is(gBreakpointsAdded.size, 0,
       "No breakpoints currently added.");
     is(gBreakpointsRemoving.size, 0,
       "No breakpoints currently being removed.");
     is(gEditor.getBreakpoints().length, 0,
       "No breakpoints currently shown in the editor.");
 
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAdd);
+    gEditor.on("breakpointAdded", onEditorBreakpointAdd);
     gPanel.addBreakpoint({ url: gSources.selectedValue, line: 4 }).then(onBreakpointAdd);
   }
 
   let onBpDebuggerAdd = false;
   let onBpEditorAdd = false;
 
   function onBreakpointAdd(aBreakpointClient) {
     ok(aBreakpointClient,
@@ -54,18 +54,18 @@ function test() {
       "Requested location url is correct");
     is(aBreakpointClient.requestedLocation.line, 4,
       "Requested location line is correct");
 
     onBpDebuggerAdd = true;
     maybeFinish();
   }
 
-  function onEditorBreakpointAdd(aEvent) {
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAdd);
+  function onEditorBreakpointAdd() {
+    gEditor.off("breakpointAdded", onEditorBreakpointAdd);
 
     is(gEditor.getBreakpoints().length, 1,
       "There is only one breakpoint in the editor");
 
     ok(!gBreakpoints._getAdded({ url: gSources.selectedValue, line: 4 }),
       "There isn't any breakpoint added on an invalid line.");
     ok(!gBreakpoints._getRemoving({ url: gSources.selectedValue, line: 4 }),
       "There isn't any breakpoint removed from an invalid line.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js
@@ -7,24 +7,23 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(2);
 
   let gTab, gDebuggee, gPanel, gDebugger;
-  let gEditor, gSources, gBreakpoints;
+  let gSources, gBreakpoints;
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gBreakpoints = gDebugger.DebuggerController.Breakpoints;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(performTestWhileNotPaused)
       .then(performTestWhilePaused)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js
@@ -35,17 +35,17 @@ function test() {
       yield testWhenBreakpointEnabledAndSecondSourceShown();
 
       yield resumeDebuggerThenCloseAndFinish(aPanel);
     });
 
     function verifyView({ disabled, visible }) {
       return Task.spawn(function() {
         // It takes a tick for the checkbox in the SideMenuWidget and the
-        // gutter in the SourceEditor to get updated.
+        // gutter in the editor to get updated.
         yield waitForTick();
 
         let breakpointItem = gSources.getBreakpoint(gBreakpointLocation);
         let visibleBreakpoints = gEditor.getBreakpoints();
         is(!!breakpointItem.attachment.disabled, disabled,
           "The selected brekapoint state was correct.");
         is(breakpointItem.attachment.view.checkbox.hasAttribute("checked"), !disabled,
           "The selected brekapoint's checkbox state was correct.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
@@ -49,35 +49,30 @@ function test() {
     ok(!gBreakpoints._getRemoving({ url: "bar", line: 3 }),
       "_getRemoving('bar', 3) returns falsey.");
 
     is(gSources.values[1], gSources.selectedValue,
       "The second source should be currently selected.");
 
     info("Add the first breakpoint.");
     let location = { url: gSources.selectedValue, line: 6 };
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst);
+    gEditor.once("breakpointAdded", onEditorBreakpointAddFirst);
     gPanel.addBreakpoint(location).then(onBreakpointAddFirst);
   }
 
   let breakpointsAdded = 0;
   let breakpointsRemoved = 0;
   let editorBreakpointChanges = 0;
 
-  function onEditorBreakpointAddFirst(aEvent) {
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst);
+  function onEditorBreakpointAddFirst(aEvent, aLine) {
     editorBreakpointChanges++;
 
     ok(aEvent,
       "breakpoint1 added to the editor.");
-    is(aEvent.added.length, 1,
-      "One breakpoint added to the editor.");
-    is(aEvent.removed.length, 0,
-      "No breakpoint was removed from the editor.");
-    is(aEvent.added[0].line, 5,
+    is(aLine, 5,
       "Editor breakpoint line is correct.");
 
     is(gEditor.getBreakpoints().length, 1,
       "editor.getBreakpoints().length is correct.");
   }
 
   function onBreakpointAddFirst(aBreakpointClient) {
     breakpointsAdded++;
@@ -103,31 +98,26 @@ function test() {
       is(aClient, aBreakpointClient,
         "_getAdded() returns the correct breakpoint.");
     });
 
     is(gSources.values[1], gSources.selectedValue,
       "The second source should be currently selected.");
 
     info("Remove the first breakpoint.");
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst);
+    gEditor.once("breakpointRemoved", onEditorBreakpointRemoveFirst);
     gPanel.removeBreakpoint(aBreakpointClient.location).then(onBreakpointRemoveFirst);
   }
 
-  function onEditorBreakpointRemoveFirst(aEvent) {
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst);
+  function onEditorBreakpointRemoveFirst(aEvent, aLine) {
     editorBreakpointChanges++;
 
     ok(aEvent,
       "breakpoint1 removed from the editor.");
-    is(aEvent.added.length, 0,
-      "No breakpoint was added to the editor.");
-    is(aEvent.removed.length, 1,
-      "One breakpoint was removed from the editor.");
-    is(aEvent.removed[0].line, 5,
+    is(aLine, 5,
       "Editor breakpoint line is correct.");
 
     is(gEditor.getBreakpoints().length, 0,
       "editor.getBreakpoints().length is correct.");
   }
 
   function onBreakpointRemoveFirst(aLocation) {
     breakpointsRemoved++;
@@ -156,26 +146,24 @@ function test() {
       "_getRemoving('gSources.selectedValue', 6) returns falsey.");
 
     is(gSources.values[1], gSources.selectedValue,
       "The second source should be currently selected.");
 
     info("Add a breakpoint to the first source, which is not selected.");
     let location = { url: gSources.values[0], line: 5 };
     let options = { noEditorUpdate: true };
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap);
+    gEditor.on("breakpointAdded", onEditorBreakpointAddBackgroundTrap);
     gPanel.addBreakpoint(location, options).then(onBreakpointAddBackground);
   }
 
-  function onEditorBreakpointAddBackgroundTrap(aEvent) {
+  function onEditorBreakpointAddBackgroundTrap() {
     // Trap listener: no breakpoint must be added to the editor when a
     // breakpoint is added to a source that is not currently selected.
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap);
     editorBreakpointChanges++;
-
     ok(false, "breakpoint2 must not be added to the editor.");
   }
 
   function onBreakpointAddBackground(aBreakpointClient, aResponseError) {
     breakpointsAdded++;
 
     ok(aBreakpointClient,
       "breakpoint2 added, client received");
@@ -198,98 +186,81 @@ function test() {
       is(aClient, aBreakpointClient,
         "_getAdded() returns the correct breakpoint.");
     });
 
     is(gSources.values[1], gSources.selectedValue,
       "The second source should be currently selected.");
 
     // Remove the trap listener.
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap);
+    gEditor.off("breakpointAdded", onEditorBreakpointAddBackgroundTrap);
 
     info("Switch to the first source, which is not yet selected");
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch);
-    gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged);
+    gEditor.once("breakpointAdded", onEditorBreakpointAddSwitch);
+    gEditor.once("change", onEditorTextChanged);
     gSources.selectedIndex = 0;
   }
 
-  function onEditorBreakpointAddSwitch(aEvent) {
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch);
+  function onEditorBreakpointAddSwitch(aEvent, aLine) {
     editorBreakpointChanges++;
 
     ok(aEvent,
       "breakpoint2 added to the editor.");
-    is(aEvent.added.length, 1,
-      "One breakpoint added to the editor.");
-    is(aEvent.removed.length, 0,
-      "No breakpoint was removed from the editor.");
-    is(aEvent.added[0].line, 4,
+    is(aLine, 4,
       "Editor breakpoint line is correct.");
 
     is(gEditor.getBreakpoints().length, 1,
       "editor.getBreakpoints().length is correct");
   }
 
   function onEditorTextChanged() {
     // Wait for the actual text to be shown.
-    if (gEditor.getText() != gDebugger.L10N.getStr("loadingText")) {
-      onEditorTextReallyChanged();
-    }
-  }
-
-  function onEditorTextReallyChanged() {
-    gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged);
+    if (gEditor.getText() == gDebugger.L10N.getStr("loadingText"))
+      return void gEditor.once("change", onEditorTextChanged);
 
     is(gEditor.getText().indexOf("debugger"), -1,
       "The second source is no longer displayed.");
     is(gEditor.getText().indexOf("firstCall"), 118,
       "The first source is displayed.");
 
     is(gSources.values[0], gSources.selectedValue,
       "The first source should be currently selected.");
 
-    let window = gEditor.editorElement.contentWindow;
+    let window = gEditor.container.contentWindow;
     executeSoon(() => window.mozRequestAnimationFrame(onReadyForClick));
   }
 
   function onReadyForClick() {
     info("Remove the second breakpoint using the mouse.");
-    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond);
+    gEditor.once("breakpointRemoved", onEditorBreakpointRemoveSecond);
 
-    let iframe = gEditor.editorElement;
+    let iframe = gEditor.container;
     let testWin = iframe.ownerDocument.defaultView;
 
     // Flush the layout for the iframe.
     info("rect " + iframe.contentDocument.documentElement.getBoundingClientRect());
 
     let utils = testWin
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils);
 
-    let lineOffset = gEditor.getLineStart(4);
-    let coords = gEditor.getLocationAtOffset(lineOffset);
-
+    let coords = gEditor.cursorCoords({ line: 4, ch: 0 });
     let rect = iframe.getBoundingClientRect();
     let left = rect.left + 10;
-    let top = rect.top + coords.y + 4;
+    let top = rect.top + coords.top + 4;
     utils.sendMouseEventToWindow("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEventToWindow("mouseup", left, top, 0, 1, 0, false, 0, 0);
   }
 
-  function onEditorBreakpointRemoveSecond(aEvent) {
-    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond);
+  function onEditorBreakpointRemoveSecond(aEvent, aLine) {
     editorBreakpointChanges++;
 
     ok(aEvent,
       "breakpoint2 removed from the editor.");
-    is(aEvent.added.length, 0,
-      "No breakpoint was added to the editor.");
-    is(aEvent.removed.length, 1,
-      "One breakpoint was removed from the editor.");
-    is(aEvent.removed[0].line, 4,
+    is(aLine, 4,
       "Editor breakpoint line is correct.");
 
     is(gEditor.getBreakpoints().length, 0,
       "editor.getBreakpoints().length is correct.");
 
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED).then(() => {
       finalCheck();
       closeDebuggerAndFinish(gPanel);
--- a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-02.js
@@ -176,17 +176,17 @@ function test() {
     ok(!selectedBreakpoint,
       "There should be no selected brekapoint in the sources pane for line " + aLine + ".");
 
     ok(isCaretPos(gPanel, aLine),
       "The editor caret position is not properly set.");
   }
 
   function setCaretPosition(aLine) {
-    gEditor.setCaretPosition(aLine - 1);
+    gEditor.setCursor({ line: aLine - 1, ch: 0 });
   }
 
   function setContextPosition(aLine) {
     gSources._editorContextMenuLineNumber = aLine - 1;
   }
 
   function clickOnBreakpoint(aIndex) {
     EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js
@@ -15,17 +15,17 @@ function test() {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gContextMenu = gDebugger.document.getElementById("sourceEditorContextMenu");
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest).then(null, info);
     gDebuggee.firstCall();
   });
 
   function performTest() {
     is(gDebugger.gThreadClient.state, "paused",
       "Should only be getting stack frames while paused.");
     is(gSources.itemCount, 2,
       "Found the expected number of sources.");
@@ -34,45 +34,30 @@ function test() {
     is(gSources.selectedValue, gSources.values[1],
       "The correct source is selected.");
 
     is(gEditor.getText().indexOf("\u263a"), 162,
       "Unicode characters are converted correctly.");
 
     ok(gContextMenu,
       "The source editor's context menupopup is available.");
-    ok(gEditor.readOnly,
+    ok(gEditor.isReadOnly(),
       "The source editor is read only.");
 
     gEditor.focus();
-    gEditor.setSelection(0, 10);
+    gEditor.setSelection({ line: 1, ch: 0 }, { line: 1, ch: 10 });
 
-    once(gContextMenu, "popupshown").then(testContextMenu);
-    gContextMenu.openPopup(gEditor.editorElement, "overlap", 0, 0, true, false);
+    once(gContextMenu, "popupshown").then(testContextMenu).then(null, info);
+    gContextMenu.openPopup(gEditor.container, "overlap", 0, 0, true, false);
   }
 
   function testContextMenu() {
     let document = gDebugger.document;
 
     ok(document.getElementById("editMenuCommands"),
       "#editMenuCommands found.");
     ok(!document.getElementById("editMenuKeys"),
       "#editMenuKeys not found.");
-    ok(document.getElementById("sourceEditorCommands"),
-      "#sourceEditorCommands found.");
-
-    // Map command ids to their expected disabled state.
-    let commands = {"se-cmd-undo": true, "se-cmd-redo": true,
-                    "se-cmd-cut": true, "se-cmd-paste": true,
-                    "se-cmd-delete": true, "cmd_findAgain": true,
-                    "cmd_findPrevious": true, "cmd_find": false,
-                    "cmd_gotoLine": false, "cmd_copy": false,
-                    "se-cmd-selectAll": false};
-
-    for (let id in commands) {
-      is(document.getElementById(id).hasAttribute("disabled"), commands[id],
-        "The element with id: " + id + " hasAttribute('disabled').");
-    }
 
     gContextMenu.hidePopup();
     resumeDebuggerThenCloseAndFinish(gPanel);
   }
 }
--- a/browser/devtools/debugger/test/browser_dbg_editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_editor-mode.js
@@ -32,17 +32,17 @@ function test() {
     gDebuggee.firstCall();
   });
 }
 
 function testInitialSource() {
   is(gSources.itemCount, 3,
     "Found the expected number of sources.");
 
-  is(gEditor.getMode(), SourceEditor.MODES.TEXT,
+  is(gEditor.getMode().name, "text",
     "Found the expected editor mode.");
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 141,
     "The second source is displayed.");
   is(gEditor.getText().search(/banana/), -1,
     "The third source is not displayed.");
 
@@ -50,17 +50,17 @@ function testInitialSource() {
   gSources.selectedLabel = "code_script-switching-01.js";
   return finished;
 }
 
 function testSwitch1() {
   is(gSources.itemCount, 3,
     "Found the expected number of sources.");
 
-  is(gEditor.getMode(), SourceEditor.MODES.JAVASCRIPT,
+  is(gEditor.getMode().name, "javascript",
     "Found the expected editor mode.");
   is(gEditor.getText().search(/firstCall/), 118,
     "The first source is displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
   is(gEditor.getText().search(/banana/), -1,
     "The third source is not displayed.");
 
@@ -68,17 +68,17 @@ function testSwitch1() {
   gSources.selectedLabel = "doc_editor-mode.html";
   return finished;
 }
 
 function testSwitch2() {
   is(gSources.itemCount, 3,
     "Found the expected number of sources.");
 
-  is(gEditor.getMode(), SourceEditor.MODES.HTML,
+  is(gEditor.getMode().name, "htmlmixed",
     "Found the expected editor mode.");
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
   is(gEditor.getText().search(/banana/), 443,
     "The third source is displayed.");
 }
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-02.js
@@ -34,17 +34,17 @@ function test() {
   });
 }
 
 function selectContextMenuItem() {
   once(gContextMenu, "popupshown").then(() => {
     const menuItem = gDebugger.document.getElementById("se-dbg-cMenu-prettyPrint");
     menuItem.click();
   });
-  gContextMenu.openPopup(gEditor.editorElement, "overlap", 0, 0, true, false);
+  gContextMenu.openPopup(gEditor.container, "overlap", 0, 0, true, false);
 }
 
 function testSourceIsPretty() {
   ok(gEditor.getText().contains("\n    "),
      "The source should be pretty printed.")
 }
 
 registerCleanupFunction(function() {
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-05.js
@@ -19,17 +19,17 @@ function test() {
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gControllerSources = gDebugger.DebuggerController.SourceScripts;
 
     Task.spawn(function() {
       yield waitForSourceShown(gPanel, TAB_URL);
 
       // From this point onward, the source editor's text should never change.
-      once(gEditor, SourceEditor.EVENTS.TEXT_CHANGED).then(() => {
+      gEditor.once("change", () => {
         ok(false, "The source editor text shouldn't have changed.");
       });
 
       is(gSources.selectedValue, TAB_URL,
         "The correct source is currently selected.");
       ok(gEditor.getText().contains("myFunction"),
         "The source shouldn't be pretty printed yet.");
 
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-06.js
@@ -35,17 +35,17 @@ function test() {
         return aOriginalRequestMethod(aPacket, aCallback);
       };
     }(gClient.request));
 
     Task.spawn(function() {
       yield waitForSourceShown(gPanel, JS_URL);
 
       // From this point onward, the source editor's text should never change.
-      once(gEditor, SourceEditor.EVENTS.TEXT_CHANGED).then(() => {
+      gEditor.once("change", () => {
         ok(false, "The source editor text shouldn't have changed.");
       });
 
       is(gSources.selectedValue, JS_URL,
         "The correct source is currently selected.");
       ok(gEditor.getText().contains("myFunction"),
         "The source shouldn't be pretty printed yet.");
 
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js
@@ -99,17 +99,17 @@ function testSwitchPaused1() {
     "The first source is displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
     ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), -1,
+    is(gEditor.getDebugLocation(), null,
       "Editor debugger location is correct.");
 
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
     gSources.selectedIndex = 1;
   });
 
   return deferred.promise;
 }
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js
@@ -94,17 +94,18 @@ function testSwitchPaused1() {
     "The first source is displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
     ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), -1,
+
+    is(gEditor.getDebugLocation(), null,
       "Editor debugger location is correct.");
 
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
     gSources.selectedLabel = gLabel2;
   });
 
   return deferred.promise;
 }
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-01.js
@@ -125,17 +125,16 @@ function performTest() {
     "The editor didn't jump to the correct token (5).");
 
   EventUtils.sendKey("UP", gDebugger);
   is(gFiltering.searchData.toSource(), '["#", ["", "debugger"]]',
     "The searchbox data wasn't parsed correctly.");
   ok(isCaretPos(gPanel, 26, 11 + token.length),
     "The editor didn't jump to the correct token (6).");
 
-
   setText(gSearchBox, ":bogus#" + token + ";");
   is(gFiltering.searchData.toSource(), '["#", [":bogus", "debugger;"]]',
     "The searchbox data wasn't parsed correctly.");
   ok(isCaretPos(gPanel, 14, 9 + token.length + 1),
     "The editor didn't jump to the correct token (7).");
 
   setText(gSearchBox, ":13#" + token + ";");
   is(gFiltering.searchData.toSource(), '["#", [":13", "debugger;"]]',
@@ -278,28 +277,28 @@ function performTest() {
     "The searchbox data wasn't parsed correctly.");
   ok(isCaretPos(gPanel, 26, 11 + token.length),
     "The editor didn't remain at the correct token (29).");
   is(gSources.visibleItems.length, 1,
     "Not all the sources are shown after the search (30).");
 
 
   gEditor.focus();
-  gEditor.setSelection(1, 5);
+  gEditor.setSelection.apply(gEditor, gEditor.getPosition(1, 5));
   ok(isCaretPos(gPanel, 1, 6),
     "The editor caret position didn't update after selecting some text.");
 
   EventUtils.synthesizeKey("F", { accelKey: true });
   is(gFiltering.searchData.toSource(), '["#", ["", "!-- "]]',
     "The searchbox data wasn't parsed correctly.");
   is(gSearchBox.value, "#!-- ",
     "The search field has the right initial value (1).");
 
   gEditor.focus();
-  gEditor.setSelection(415, 418);
+  gEditor.setSelection.apply(gEditor, gEditor.getPosition(415, 418));
   ok(isCaretPos(gPanel, 21, 30),
     "The editor caret position didn't update after selecting some number.");
 
   EventUtils.synthesizeKey("L", { accelKey: true });
   is(gFiltering.searchData.toSource(), '[":", ["", 100]]',
     "The searchbox data wasn't parsed correctly.");
   is(gSearchBox.value, ":100",
     "The search field has the right initial value (2).");
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-04.js
@@ -28,99 +28,99 @@ function test() {
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
 function testLineSearch() {
   setText(gSearchBox, ":42");
-  ok(isCaretPos(gPanel, 1),
+  ok(isCaretPos(gPanel, 7),
     "The editor caret position appears to be correct (1.1).");
-  ok(isEditorSel(gPanel, [1, 1]),
+  ok(isEditorSel(gPanel, [160, 160]),
     "The editor selection appears to be correct (1.1).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (1.1).");
 
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 4),
     "The editor caret position appears to be correct (1.2).");
   ok(isEditorSel(gPanel, [110, 110]),
     "The editor selection appears to be correct (1.2).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (1.2).");
 
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 4),
     "The editor caret position appears to be correct (1.3).");
   ok(isEditorSel(gPanel, [110, 110]),
     "The editor selection appears to be correct (1.3).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (1.3).");
 
   setText(gSearchBox, ":4");
   ok(isCaretPos(gPanel, 4),
     "The editor caret position appears to be correct (1.4).");
   ok(isEditorSel(gPanel, [110, 110]),
     "The editor selection appears to be correct (1.4).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (1.4).");
 
   gSearchBox.select();
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 4),
     "The editor caret position appears to be correct (1.5).");
   ok(isEditorSel(gPanel, [110, 110]),
     "The editor selection appears to be correct (1.5).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (1.5).");
   is(gSearchBox.value, "",
     "The searchbox should have been cleared.");
 }
 
 function testTokenSearch() {
   setText(gSearchBox, "#;\"");
   ok(isCaretPos(gPanel, 5, 23),
     "The editor caret position appears to be correct (2.1).");
   ok(isEditorSel(gPanel, [153, 155]),
     "The editor selection appears to be correct (2.1).");
-  is(gEditor.getSelectedText(), ";\"",
+  is(gEditor.getSelection(), ";\"",
     "The editor selected text appears to be correct (2.1).");
 
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 5, 22),
     "The editor caret position appears to be correct (2.2).");
   ok(isEditorSel(gPanel, [153, 154]),
     "The editor selection appears to be correct (2.2).");
-  is(gEditor.getSelectedText(), ";",
+  is(gEditor.getSelection(), ";",
     "The editor selected text appears to be correct (2.2).");
 
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 5, 22),
     "The editor caret position appears to be correct (2.3).");
   ok(isEditorSel(gPanel, [154, 154]),
     "The editor selection appears to be correct (2.3).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (2.3).");
 
   setText(gSearchBox, "#;");
   ok(isCaretPos(gPanel, 5, 22),
     "The editor caret position appears to be correct (2.4).");
   ok(isEditorSel(gPanel, [153, 154]),
     "The editor selection appears to be correct (2.4).");
-  is(gEditor.getSelectedText(), ";",
+  is(gEditor.getSelection(), ";",
     "The editor selected text appears to be correct (2.4).");
 
   gSearchBox.select();
   backspaceText(gSearchBox, 1);
   ok(isCaretPos(gPanel, 5, 22),
     "The editor caret position appears to be correct (2.5).");
   ok(isEditorSel(gPanel, [154, 154]),
     "The editor selection appears to be correct (2.5).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct (2.5).");
   is(gSearchBox.value, "",
     "The searchbox should have been cleared.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
--- a/browser/devtools/debugger/test/browser_dbg_search-global-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-02.js
@@ -82,17 +82,17 @@ function doFirstJump() {
       "The currently shown source is incorrect (1).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (1).");
 
     // The editor's selected text takes a tick to update.
     executeSoon(() => {
       ok(isCaretPos(gPanel, 5, 7),
         "The editor didn't jump to the correct line (1).");
-      is(gEditor.getSelectedText(), "eval",
+      is(gEditor.getSelection(), "eval",
         "The editor didn't select the correct text (1).");
 
       deferred.resolve();
     });
   });
 
   EventUtils.sendKey("DOWN", gDebugger);
 
@@ -110,17 +110,17 @@ function doSecondJump() {
       "The currently shown source is incorrect (2).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (2).");
 
     // The editor's selected text takes a tick to update.
     executeSoon(() => {
       ok(isCaretPos(gPanel, 6, 7),
         "The editor didn't jump to the correct line (2).");
-      is(gEditor.getSelectedText(), "eval",
+      is(gEditor.getSelection(), "eval",
         "The editor didn't select the correct text (2).");
 
       deferred.resolve();
     });
   });
 
   EventUtils.sendKey("DOWN", gDebugger);
 
@@ -138,17 +138,17 @@ function doWrapAroundJump() {
       "The currently shown source is incorrect (3).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (3).");
 
     // The editor's selected text takes a tick to update.
     executeSoon(() => {
       ok(isCaretPos(gPanel, 5, 7),
         "The editor didn't jump to the correct line (3).");
-      is(gEditor.getSelectedText(), "eval",
+      is(gEditor.getSelection(), "eval",
         "The editor didn't select the correct text (3).");
 
       deferred.resolve();
     });
   });
 
   EventUtils.sendKey("DOWN", gDebugger);
 
@@ -166,17 +166,17 @@ function doBackwardsWrapAroundJump() {
       "The currently shown source is incorrect (4).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (4).");
 
     // The editor's selected text takes a tick to update.
     executeSoon(() => {
       ok(isCaretPos(gPanel, 6, 7),
         "The editor didn't jump to the correct line (4).");
-      is(gEditor.getSelectedText(), "eval",
+      is(gEditor.getSelection(), "eval",
         "The editor didn't select the correct text (4).");
 
       deferred.resolve();
     });
   });
 
   EventUtils.sendKey("UP", gDebugger);
 
@@ -190,17 +190,17 @@ function testSearchTokenEmpty() {
   info("Debugger editor text:\n" + gEditor.getText());
 
   ok(gSources.selectedValue.contains("-02.js"),
     "The currently shown source is incorrect (4).");
   is(gSources.visibleItems.length, 2,
     "Not all the sources are shown after the global search (4).");
   ok(isCaretPos(gPanel, 6, 7),
     "The editor didn't remain at the correct line (4).");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor shouldn't keep the previous text selected (4).");
 
   is(gSearchView.itemCount, 0,
     "The global search pane shouldn't have any child nodes after clearing.");
   is(gSearchView.widget._parent.hidden, true,
     "The global search pane shouldn't be visible after clearing.");
   is(gSearchView._splitter.hidden, true,
     "The global search pane splitter shouldn't be visible after clearing.");
--- a/browser/devtools/debugger/test/browser_dbg_search-global-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-05.js
@@ -95,17 +95,17 @@ function testClickLineToJump() {
   let firstLine = sourceResults[0].querySelector(".dbg-results-line-contents");
 
   waitForSourceAndCaret(gPanel, "-01.js", 1, 5).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(isCaretPos(gPanel, 1, 5),
       "The editor didn't jump to the correct line (1).");
-    is(gEditor.getSelectedText(), "A",
+    is(gEditor.getSelection(), "A",
       "The editor didn't select the correct text (1).");
     ok(gSources.selectedValue.contains("-01.js"),
       "The currently shown source is incorrect (1).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (1).");
 
     deferred.resolve();
   });
@@ -124,17 +124,17 @@ function testClickMatchToJump() {
   let lastMatch = Array.slice(secondMatches).pop();
 
   waitForSourceAndCaret(gPanel, "-02.js", 6, 6).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(isCaretPos(gPanel, 6, 6),
       "The editor didn't jump to the correct line (2).");
-    is(gEditor.getSelectedText(), "a",
+    is(gEditor.getSelection(), "a",
       "The editor didn't select the correct text (2).");
     ok(gSources.selectedValue.contains("-02.js"),
       "The currently shown source is incorrect (2).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (2).");
 
     deferred.resolve();
   });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-sources-01.js
@@ -3,28 +3,27 @@
 
 /**
  * Tests basic functionality of sources filtering (file search).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 let gTab, gDebuggee, gPanel, gDebugger;
-let gEditor, gSources, gSearchView, gSearchBox;
+let gSources, gSearchView, gSearchBox;
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(3);
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.FilteredSources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(bogusSearch)
       .then(firstSearch)
       .then(secondSearch)
@@ -225,13 +224,12 @@ function verifyContents(aArgs) {
     "No sources should be displayed in the sources container after a bogus search.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
-  gEditor = null;
   gSources = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-sources-02.js
@@ -3,28 +3,27 @@
 
 /**
  * Tests more complex functionality of sources filtering (file search).
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_editor-mode.html";
 
 let gTab, gDebuggee, gPanel, gDebugger;
-let gEditor, gSources, gSourceUtils, gSearchView, gSearchBox;
+let gSources, gSourceUtils, gSearchView, gSearchBox;
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(3);
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSourceUtils = gDebugger.SourceUtils;
     gSearchView = gDebugger.DebuggerView.FilteredSources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     waitForSourceShown(gPanel, "-01.js")
       .then(firstSearch)
       .then(secondSearch)
@@ -267,14 +266,13 @@ function verifyContents(aMatches) {
   }
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
-  gEditor = null;
   gSources = null;
   gSourceUtils = null;
   gSearchView = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
@@ -4,25 +4,24 @@
 /**
  * Make sure that the searchbox popup is displayed when focusing the searchbox,
  * and hidden when the user starts typing.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 let gTab, gDebuggee, gPanel, gDebugger;
-let gEditor, gSearchBox, gSearchBoxPanel;
+let gSearchBox, gSearchBoxPanel;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel;
 
     waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
       .then(showPopup)
       .then(hidePopup)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
@@ -51,12 +50,11 @@ function hidePopup() {
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
-  gEditor = null;
   gSearchBox = null;
   gSearchBoxPanel = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
@@ -39,17 +39,17 @@ function test() {
 }
 
 function tryShowPopup() {
   setText(gSearchBox, "#call()");
   ok(isCaretPos(gPanel, 4, 22),
     "The editor caret position appears to be correct.");
   ok(isEditorSel(gPanel, [125, 131]),
     "The editor selection appears to be correct.");
-  is(gEditor.getSelectedText(), "Call()",
+  is(gEditor.getSelection(), "Call()",
     "The editor selected text appears to be correct.");
 
   is(gSearchBoxPanel.state, "closed",
     "The search box panel shouldn't be visible yet.");
 
   EventUtils.sendMouseEvent({ type: "click" }, gSearchBox, gDebugger);
 }
 
@@ -63,17 +63,17 @@ function focusEditor() {
   return deferred.promise;
 }
 
 function testFocusLost() {
   ok(isCaretPos(gPanel, 6, 1),
     "The editor caret position appears to be correct after gaining focus.");
   ok(isEditorSel(gPanel, [165, 165]),
     "The editor selection appears to be correct after gaining focus.");
-  is(gEditor.getSelectedText(), "",
+  is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct after gaining focus.");
 
   is(gSearchBoxPanel.state, "closed",
     "The search box panel should still not be visible.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
--- a/browser/devtools/debugger/test/browser_dbg_sources-labels.js
+++ b/browser/devtools/debugger/test/browser_dbg_sources-labels.js
@@ -80,17 +80,17 @@ function test() {
       "No item in the sources widget should be selected (2).");
     is(gSources.selectedValue, "",
       "No item in the sources widget should be selected (3).");
 
     for (let { href, leaf } of urls) {
       let url = href + leaf;
       let label = gUtils.getSourceLabel(url);
       let trimmedLabel = gUtils.trimUrlLength(label);
-      gSources.push([trimmedLabel, url]);
+      gSources.push([trimmedLabel, url], { attachment: {}});
     }
 
     info("Source labels:");
     info(gSources.labels.toSource());
 
     info("Source locations:");
     info(gSources.values.toSource());
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js
@@ -127,17 +127,17 @@ function testOldestTwoFrames() {
 function testAfterResume() {
   let deferred = promise.defer();
 
   gDebugger.once(gDebugger.EVENTS.AFTER_FRAMES_CLEARED, () => {
     is(gFrames.itemCount, 0,
       "Should have no frames after resume.");
     ok(isCaretPos(gPanel, 5),
       "Editor caret location is correct after resume.");
-    is(gEditor.getDebugLocation(), -1,
+    is(gEditor.getDebugLocation(), null,
       "Editor debug location is correct after resume.");
 
     deferred.resolve();
   }, true);
 
   gDebugger.gThreadClient.resume();
 
   return deferred.promise;
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-filter-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-filter-05.js
@@ -4,28 +4,27 @@
 /**
  * Make sure that the variables view correctly shows/hides nodes when various
  * keyboard shortcuts are pressed in the debugger's searchbox.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_with-frame.html";
 
 let gTab, gDebuggee, gPanel, gDebugger;
-let gEditor, gVariables, gSearchBox;
+let gVariables, gSearchBox;
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(2);
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gVariables = gDebugger.DebuggerView.Variables;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     // The first 'with' scope should be expanded by default, but the
     // variables haven't been fetched yet. This is how 'with' scopes work.
     promise.all([
       waitForSourceAndCaret(gPanel, ".html", 22),
       waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
@@ -228,12 +227,11 @@ function prepareVariablesAndProperties()
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
-  gEditor = null;
   gVariables = null;
   gSearchBox = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js
@@ -7,24 +7,23 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_watch-expressions.html";
 
 function test() {
   // Debug test slaves are a bit slow at this test.
   requestLongerTimeout(2);
 
   let gTab, gDebuggee, gPanel, gDebugger;
-  let gEditor, gWatch, gVariables;
+  let gWatch, gVariables;
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gEditor = gDebugger.DebuggerView.editor;
     gWatch = gDebugger.DebuggerView.WatchExpressions;
     gVariables = gDebugger.DebuggerView.Variables;
 
     gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
 
     waitForSourceShown(gPanel, ".html", 1)
       .then(() => addExpressions())
       .then(() => performTest())
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -15,17 +15,16 @@ Services.prefs.setBoolPref("devtools.deb
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 let { BrowserDebuggerProcess } = Cu.import("resource:///modules/devtools/DebuggerProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
-let { SourceEditor } = Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 let Toolbox = devtools.Toolbox;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 
 // All tests are asynchronous.
 waitForExplicitFinish();
@@ -241,19 +240,19 @@ function ensureSourceIs(aPanel, aUrl, aW
   if (aWaitFlag) {
     return waitForSourceShown(aPanel, aUrl);
   }
   ok(false, "Expected source was not already shown: " + aUrl);
   return promise.reject(null);
 }
 
 function waitForCaretUpdated(aPanel, aLine, aCol = 1) {
-  return waitForEditorEvents(aPanel, SourceEditor.EVENTS.SELECTION).then(() => {
-    let caret = aPanel.panelWin.DebuggerView.editor.getCaretPosition();
-    info("Caret updated: " + (caret.line + 1) + ", " + (caret.col + 1));
+  return waitForEditorEvents(aPanel, "cursorActivity").then(() => {
+    let cursor = aPanel.panelWin.DebuggerView.editor.getCursor();
+    info("Caret updated: " + (cursor.line + 1) + ", " + (cursor.ch + 1));
 
     if (!isCaretPos(aPanel, aLine, aCol)) {
       return waitForCaretUpdated(aPanel, aLine, aCol);
     } else {
       ok(true, "The correct caret position has been set.");
     }
   });
 }
@@ -267,26 +266,29 @@ function ensureCaretAt(aPanel, aLine, aC
     return waitForCaretUpdated(aPanel, aLine, aCol);
   }
   ok(false, "Expected caret position was not already set: " + aLine + "," + aCol);
   return promise.reject(null);
 }
 
 function isCaretPos(aPanel, aLine, aCol = 1) {
   let editor = aPanel.panelWin.DebuggerView.editor;
-  let caret = editor.getCaretPosition();
+  let cursor = editor.getCursor();
 
   // Source editor starts counting line and column numbers from 0.
-  info("Current editor caret position: " + (caret.line + 1) + ", " + (caret.col + 1));
-  return caret.line == (aLine - 1) && caret.col == (aCol - 1);
+  info("Current editor caret position: " + (cursor.line + 1) + ", " + (cursor.ch + 1));
+  return cursor.line == (aLine - 1) && cursor.ch == (aCol - 1);
 }
 
 function isEditorSel(aPanel, [start, end]) {
   let editor = aPanel.panelWin.DebuggerView.editor;
-  let range = editor.getSelection();
+  let range = {
+    start: editor.getOffset(editor.getCursor("start")),
+    end:   editor.getOffset(editor.getCursor())
+  };
 
   // Source editor starts counting line and column numbers from 0.
   info("Current editor selection: " + (range.start + 1) + ", " + (range.end + 1));
   return range.start == (start - 1) && range.end == (end - 1);
 }
 
 function waitForSourceAndCaret(aPanel, aUrl, aLine, aCol) {
   return promise.all([
@@ -331,22 +333,22 @@ function waitForDebuggerEvents(aPanel, a
 
 function waitForEditorEvents(aPanel, aEventName, aEventRepeat = 1) {
   info("Waiting for editor event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
 
   let deferred = promise.defer();
   let editor = aPanel.panelWin.DebuggerView.editor;
   let count = 0;
 
-  editor.addEventListener(aEventName, function onEvent(...aArgs) {
+  editor.on(aEventName, function onEvent(...aArgs) {
     info("Editor event '" + aEventName + "' fired: " + (++count) + " time(s).");
 
     if (count == aEventRepeat) {
       ok(true, "Enough '" + aEventName + "' editor events have been fired.");
-      editor.removeEventListener(aEventName, onEvent);
+      editor.off(aEventName, onEvent);
       deferred.resolve.apply(deferred, aArgs);
     }
   });
 
   return deferred.promise;
 }
 
 function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) {
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -27,22 +27,27 @@ browser.jar:
     content/browser/devtools/layoutview/view.css                       (layoutview/view.css)
     content/browser/devtools/fontinspector/font-inspector.js           (fontinspector/font-inspector.js)
     content/browser/devtools/fontinspector/font-inspector.xhtml        (fontinspector/font-inspector.xhtml)
     content/browser/devtools/fontinspector/font-inspector.css          (fontinspector/font-inspector.css)
     content/browser/devtools/orion.js                                  (sourceeditor/orion/orion.js)
     content/browser/devtools/codemirror/codemirror.js                  (sourceeditor/codemirror/codemirror.js)
     content/browser/devtools/codemirror/codemirror.css                 (sourceeditor/codemirror/codemirror.css)
     content/browser/devtools/codemirror/javascript.js                  (sourceeditor/codemirror/javascript.js)
+    content/browser/devtools/codemirror/xml.js                         (sourceeditor/codemirror/xml.js)
+    content/browser/devtools/codemirror/css.js                         (sourceeditor/codemirror/css.js)
+    content/browser/devtools/codemirror/htmlmixed.js                   (sourceeditor/codemirror/htmlmixed.js)
+    content/browser/devtools/codemirror/activeline.js                  (sourceeditor/codemirror/activeline.js)
     content/browser/devtools/codemirror/matchbrackets.js               (sourceeditor/codemirror/matchbrackets.js)
     content/browser/devtools/codemirror/comment.js                     (sourceeditor/codemirror/comment.js)
     content/browser/devtools/codemirror/searchcursor.js                (sourceeditor/codemirror/search/searchcursor.js)
     content/browser/devtools/codemirror/search.js                      (sourceeditor/codemirror/search/search.js)
     content/browser/devtools/codemirror/dialog.js                      (sourceeditor/codemirror/dialog/dialog.js)
     content/browser/devtools/codemirror/dialog.css                     (sourceeditor/codemirror/dialog/dialog.css)
+    content/browser/devtools/codemirror/mozilla.css                    (sourceeditor/codemirror/mozilla.css)
 *   content/browser/devtools/source-editor-overlay.xul                 (sourceeditor/source-editor-overlay.xul)
     content/browser/devtools/debugger.xul                              (debugger/debugger.xul)
     content/browser/devtools/debugger.css                              (debugger/debugger.css)
     content/browser/devtools/debugger-controller.js                    (debugger/debugger-controller.js)
     content/browser/devtools/debugger-view.js                          (debugger/debugger-view.js)
     content/browser/devtools/debugger-toolbar.js                       (debugger/debugger-toolbar.js)
     content/browser/devtools/debugger-panes.js                         (debugger/debugger-panes.js)
     content/browser/devtools/profiler.xul                              (profiler/profiler.xul)
--- a/browser/devtools/profiler/test/browser_profiler_bug_834878_source_buttons.js
+++ b/browser/devtools/profiler/test/browser_profiler_bug_834878_source_buttons.js
@@ -12,23 +12,22 @@ function test() {
     let data = { uri: SCRIPT, line: 5, isChrome: false };
 
     panel.displaySource(data).then(function onOpen() {
       let target = TargetFactory.forTab(tab);
       let dbg = gDevTools.getToolbox(target).getPanel("jsdebugger");
       let view = dbg.panelWin.DebuggerView;
 
       is(view.Sources.selectedValue, data.uri, "URI is different");
-      is(view.editor.getCaretPosition().line, data.line - 1,
-        "Line is different");
+      is(view.editor.getCursor().line, data.line - 1, "Line is different");
 
       // Test the case where script is already loaded.
-      view.editor.setCaretPosition(1);
+      view.editor.setCursor({ line: 1, ch: 1 });
       gDevTools.showToolbox(target, "jsprofiler").then(function () {
         panel.displaySource(data).then(function onOpenAgain() {
-          is(view.editor.getCaretPosition().line, data.line - 1,
+          is(view.editor.getCursor().line, data.line - 1,
             "Line is different");
           tearDown(tab);
         });
       });
     });
   });
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js
@@ -97,17 +97,17 @@ function fileAImported(aStatus, aFileCon
   ok(Components.isSuccessCode(aStatus),
      "the temporary file A was imported successfully with Scratchpad");
 
   is(aFileContent, gFileAContent, "received data is correct");
 
   is(gScratchpad.getText(), gFileAContent, "the editor content is correct");
 
   gScratchpad.editor.replaceText("new text",
-    gScratchpad.editor.posFromIndex(gScratchpad.getText().length));
+    gScratchpad.editor.getPosition(gScratchpad.getText().length));
 
   is(gScratchpad.getText(), gFileAContent + "new text", "text updated correctly");
   gScratchpad.undo();
   is(gScratchpad.getText(), gFileAContent, "undo works");
   gScratchpad.redo();
   is(gScratchpad.getText(), gFileAContent + "new text", "redo works");
 
   // Import the file B into Scratchpad.
@@ -126,17 +126,17 @@ function fileBImported(aStatus, aFileCon
 
   ok(!gScratchpad.editor.canUndo(), "editor cannot undo after load");
 
   gScratchpad.undo();
   is(gScratchpad.getText(), gFileBContent,
       "the editor content is still correct after undo");
 
   gScratchpad.editor.replaceText("new text",
-    gScratchpad.editor.posFromIndex(gScratchpad.getText().length));
+    gScratchpad.editor.getPosition(gScratchpad.getText().length));
   is(gScratchpad.getText(), gFileBContent + "new text", "text updated correctly");
 
   gScratchpad.undo();
   is(gScratchpad.getText(), gFileBContent, "undo works");
   ok(!gScratchpad.editor.canUndo(), "editor cannot undo after load (again)");
 
   gScratchpad.redo();
   is(gScratchpad.getText(), gFileBContent + "new text", "redo works");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/codemirror/activeline.js
@@ -0,0 +1,40 @@
+/* vim:set ts=2 sw=2 sts=2 et tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+(function () {
+  "use strict";
+
+  const WRAP_CLASS = "CodeMirror-activeline";
+  const BACK_CLASS = "CodeMirror-activeline-background";
+
+  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+
+    if (val && !prev) {
+      updateActiveLine(cm);
+      cm.on("cursorActivity", updateActiveLine);
+    } else if (!val && prev) {
+      cm.off("cursorActivity", updateActiveLine);
+      clearActiveLine(cm);
+      delete cm.state.activeLine;
+    }
+  });
+
+  function clearActiveLine(cm) {
+    if ("activeLine" in cm.state) {
+      cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS);
+      cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS);
+    }
+  }
+
+  function updateActiveLine(cm) {
+    var line = cm.getLineHandleVisualStart(cm.getCursor().line);
+    if (cm.state.activeLine == line) return;
+    clearActiveLine(cm);
+    cm.addLineClass(line, "wrap", WRAP_CLASS);
+    cm.addLineClass(line, "background", BACK_CLASS);
+    cm.state.activeLine = line;
+  }
+})();
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/codemirror/css.js
@@ -0,0 +1,623 @@
+CodeMirror.defineMode("css", function(config) {
+  return CodeMirror.getMode(config, "text/css");
+});
+
+CodeMirror.defineMode("css-base", function(config, parserConfig) {
+  "use strict";
+
+  var indentUnit = config.indentUnit,
+      hooks = parserConfig.hooks || {},
+      atMediaTypes = parserConfig.atMediaTypes || {},
+      atMediaFeatures = parserConfig.atMediaFeatures || {},
+      propertyKeywords = parserConfig.propertyKeywords || {},
+      colorKeywords = parserConfig.colorKeywords || {},
+      valueKeywords = parserConfig.valueKeywords || {},
+      allowNested = !!parserConfig.allowNested,
+      type = null;
+
+  function ret(style, tp) { type = tp; return style; }
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (hooks[ch]) {
+      // result[0] is style and result[1] is type
+      var result = hooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+    if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
+    else if (ch == "=") ret(null, "compare");
+    else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
+    else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (ch == "#") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("atom", "hash");
+    }
+    else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    }
+    else if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    }
+    else if (ch === "-") {
+      if (/\d/.test(stream.peek())) {
+        stream.eatWhile(/[\w.%]/);
+        return ret("number", "unit");
+      } else if (stream.match(/^[^-]+-/)) {
+        return ret("meta", "meta");
+      }
+    }
+    else if (/[,+>*\/]/.test(ch)) {
+      return ret(null, "select-op");
+    }
+    else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+      return ret("qualifier", "qualifier");
+    }
+    else if (ch == ":") {
+      return ret("operator", ch);
+    }
+    else if (/[;{}\[\]\(\)]/.test(ch)) {
+      return ret(null, ch);
+    }
+    else if (ch == "u" && stream.match("rl(")) {
+      stream.backUp(1);
+      state.tokenize = tokenParenthesized;
+      return ret("property", "variable");
+    }
+    else {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("property", "variable");
+    }
+  }
+
+  function tokenString(quote, nonInclusive) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped)
+          break;
+        escaped = !escaped && ch == "\\";
+      }
+      if (!escaped) {
+        if (nonInclusive) stream.backUp(1);
+        state.tokenize = tokenBase;
+      }
+      return ret("string", "string");
+    };
+  }
+
+  function tokenParenthesized(stream, state) {
+    stream.next(); // Must be '('
+    if (!stream.match(/\s*[\"\']/, false))
+      state.tokenize = tokenString(")", true);
+    else
+      state.tokenize = tokenBase;
+    return ret(null, "(");
+  }
+
+  return {
+    startState: function(base) {
+      return {tokenize: tokenBase,
+              baseIndent: base || 0,
+              stack: [],
+              lastToken: null};
+    },
+
+    token: function(stream, state) {
+
+      // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
+      //
+      // rule** or **ruleset:
+      // A selector + braces combo, or an at-rule.
+      //
+      // declaration block:
+      // A sequence of declarations.
+      //
+      // declaration:
+      // A property + colon + value combo.
+      //
+      // property value:
+      // The entire value of a property.
+      //
+      // component value:
+      // A single piece of a property value. Like the 5px in
+      // text-shadow: 0 0 5px blue;. Can also refer to things that are
+      // multiple terms, like the 1-4 terms that make up the background-size
+      // portion of the background shorthand.
+      //
+      // term:
+      // The basic unit of author-facing CSS, like a single number (5),
+      // dimension (5px), string ("foo"), or function. Officially defined
+      //  by the CSS 2.1 grammar (look for the 'term' production)
+      //
+      //
+      // simple selector:
+      // A single atomic selector, like a type selector, an attr selector, a
+      // class selector, etc.
+      //
+      // compound selector:
+      // One or more simple selectors without a combinator. div.example is
+      // compound, div > .example is not.
+      //
+      // complex selector:
+      // One or more compound selectors chained with combinators.
+      //
+      // combinator:
+      // The parts of selectors that express relationships. There are four
+      // currently - the space (descendant combinator), the greater-than
+      // bracket (child combinator), the plus sign (next sibling combinator),
+      // and the tilda (following sibling combinator).
+      //
+      // sequence of selectors:
+      // One or more of the named type of selector chained with commas.
+
+      state.tokenize = state.tokenize || tokenBase;
+      if (state.tokenize == tokenBase && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (style && typeof style != "string") style = ret(style[0], style[1]);
+
+      // Changing style returned based on context
+      var context = state.stack[state.stack.length-1];
+      if (style == "variable") {
+        if (type == "variable-definition") state.stack.push("propertyValue");
+        return state.lastToken = "variable-2";
+      } else if (style == "property") {
+        var word = stream.current().toLowerCase();
+        if (context == "propertyValue") {
+          if (valueKeywords.hasOwnProperty(word)) {
+            style = "string-2";
+          } else if (colorKeywords.hasOwnProperty(word)) {
+            style = "keyword";
+          } else {
+            style = "variable-2";
+          }
+        } else if (context == "rule") {
+          if (!propertyKeywords.hasOwnProperty(word)) {
+            style += " error";
+          }
+        } else if (context == "block") {
+          // if a value is present in both property, value, or color, the order
+          // of preference is property -> color -> value
+          if (propertyKeywords.hasOwnProperty(word)) {
+            style = "property";
+          } else if (colorKeywords.hasOwnProperty(word)) {
+            style = "keyword";
+          } else if (valueKeywords.hasOwnProperty(word)) {
+            style = "string-2";
+          } else {
+            style = "tag";
+          }
+        } else if (!context || context == "@media{") {
+          style = "tag";
+        } else if (context == "@media") {
+          if (atMediaTypes[stream.current()]) {
+            style = "attribute"; // Known attribute
+          } else if (/^(only|not)$/.test(word)) {
+            style = "keyword";
+          } else if (word == "and") {
+            style = "error"; // "and" is only allowed in @mediaType
+          } else if (atMediaFeatures.hasOwnProperty(word)) {
+            style = "error"; // Known property, should be in @mediaType(
+          } else {
+            // Unknown, expecting keyword or attribute, assuming attribute
+            style = "attribute error";
+          }
+        } else if (context == "@mediaType") {
+          if (atMediaTypes.hasOwnProperty(word)) {
+            style = "attribute";
+          } else if (word == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/.test(word)) {
+            style = "error"; // Only allowed in @media
+          } else {
+            // Unknown attribute or property, but expecting property (preceded
+            // by "and"). Should be in parentheses
+            style = "error";
+          }
+        } else if (context == "@mediaType(") {
+          if (propertyKeywords.hasOwnProperty(word)) {
+            // do nothing, remains "property"
+          } else if (atMediaTypes.hasOwnProperty(word)) {
+            style = "error"; // Known property, should be in parentheses
+          } else if (word == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/.test(word)) {
+            style = "error"; // Only allowed in @media
+          } else {
+            style += " error";
+          }
+        } else if (context == "@import") {
+          style = "tag";
+        } else {
+          style = "error";
+        }
+      } else if (style == "atom") {
+        if(!context || context == "@media{" || context == "block") {
+          style = "builtin";
+        } else if (context == "propertyValue") {
+          if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
+            style += " error";
+          }
+        } else {
+          style = "error";
+        }
+      } else if (context == "@media" && type == "{") {
+        style = "error";
+      }
+
+      // Push/pop context stack
+      if (type == "{") {
+        if (context == "@media" || context == "@mediaType") {
+          state.stack[state.stack.length-1] = "@media{";
+        }
+        else {
+          var newContext = allowNested ? "block" : "rule";
+          state.stack.push(newContext);
+        }
+      }
+      else if (type == "}") {
+        if (context == "interpolation") style = "operator";
+        state.stack.pop();
+        if (context == "propertyValue") state.stack.pop();
+      }
+      else if (type == "interpolation") state.stack.push("interpolation");
+      else if (type == "@media") state.stack.push("@media");
+      else if (type == "@import") state.stack.push("@import");
+      else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
+        state.stack[state.stack.length-1] = "@mediaType";
+      else if (context == "@mediaType" && stream.current() == ",")
+        state.stack[state.stack.length-1] = "@media";
+      else if (type == "(") {
+        if (context == "@media" || context == "@mediaType") {
+          // Make sure @mediaType is used to avoid error on {
+          state.stack[state.stack.length-1] = "@mediaType";
+          state.stack.push("@mediaType(");
+        }
+      }
+      else if (type == ")") {
+        if (context == "propertyValue" && state.stack[state.stack.length-2] == "@mediaType(") {
+          // In @mediaType( without closing ; after propertyValue
+          state.stack.pop();
+          state.stack.pop();
+        }
+        else if (context == "@mediaType(") {
+          state.stack.pop();
+        }
+      }
+      else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
+      else if (context == "propertyValue" && type == ";") state.stack.pop();
+      else if (context == "@import" && type == ";") state.stack.pop();
+
+      return state.lastToken = style;
+    },
+
+    indent: function(state, textAfter) {
+      var n = state.stack.length;
+      if (/^\}/.test(textAfter))
+        n -= state.stack[n-1] == "propertyValue" ? 2 : 1;
+      return state.baseIndent + n * indentUnit;
+    },
+
+    electricChars: "}",
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    fold: "brace"
+  };
+});
+
+(function() {
+  function keySet(array) {
+    var keys = {};
+    for (var i = 0; i < array.length; ++i) {
+      keys[array[i]] = true;
+    }
+    return keys;
+  }
+
+  var atMediaTypes = keySet([
+    "all", "aural", "braille", "handheld", "print", "projection", "screen",
+    "tty", "tv", "embossed"
+  ]);
+
+  var atMediaFeatures = keySet([
+    "width", "min-width", "max-width", "height", "min-height", "max-height",
+    "device-width", "min-device-width", "max-device-width", "device-height",
+    "min-device-height", "max-device-height", "aspect-ratio",
+    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+    "max-color", "color-index", "min-color-index", "max-color-index",
+    "monochrome", "min-monochrome", "max-monochrome", "resolution",
+    "min-resolution", "max-resolution", "scan", "grid"
+  ]);
+
+  var propertyKeywords = keySet([
+    "align-content", "align-items", "align-self", "alignment-adjust",
+    "alignment-baseline", "anchor-point", "animation", "animation-delay",
+    "animation-direction", "animation-duration", "animation-iteration-count",
+    "animation-name", "animation-play-state", "animation-timing-function",
+    "appearance", "azimuth", "backface-visibility", "background",
+    "background-attachment", "background-clip", "background-color",
+    "background-image", "background-origin", "background-position",
+    "background-repeat", "background-size", "baseline-shift", "binding",
+    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+    "bookmark-target", "border", "border-bottom", "border-bottom-color",
+    "border-bottom-left-radius", "border-bottom-right-radius",
+    "border-bottom-style", "border-bottom-width", "border-collapse",
+    "border-color", "border-image", "border-image-outset",
+    "border-image-repeat", "border-image-slice", "border-image-source",
+    "border-image-width", "border-left", "border-left-color",
+    "border-left-style", "border-left-width", "border-radius", "border-right",
+    "border-right-color", "border-right-style", "border-right-width",
+    "border-spacing", "border-style", "border-top", "border-top-color",
+    "border-top-left-radius", "border-top-right-radius", "border-top-style",
+    "border-top-width", "border-width", "bottom", "box-decoration-break",
+    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+    "column-fill", "column-gap", "column-rule", "column-rule-color",
+    "column-rule-style", "column-rule-width", "column-span", "column-width",
+    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+    "cue-after", "cue-before", "cursor", "direction", "display",
+    "dominant-baseline", "drop-initial-after-adjust",
+    "drop-initial-after-align", "drop-initial-before-adjust",
+    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+    "float", "float-offset", "font", "font-feature-settings", "font-family",
+    "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+    "font-stretch", "font-style", "font-synthesis", "font-variant",
+    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "font-weight", "grid-cell", "grid-column", "grid-column-align",
+    "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
+    "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
+    "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
+    "icon", "image-orientation", "image-rendering", "image-resolution",
+    "inline-box-align", "justify-content", "left", "letter-spacing",
+    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+    "line-stacking-shift", "line-stacking-strategy", "list-style",
+    "list-style-image", "list-style-position", "list-style-type", "margin",
+    "margin-bottom", "margin-left", "margin-right", "margin-top",
+    "marker-offset", "marks", "marquee-direction", "marquee-loop",
+    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+    "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
+    "outline-color", "outline-offset", "outline-style", "outline-width",
+    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+    "page", "page-break-after", "page-break-before", "page-break-inside",
+    "page-policy", "pause", "pause-after", "pause-before", "perspective",
+    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+    "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
+    "resize", "rest", "rest-after", "rest-before", "richness", "right",
+    "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+    "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
+    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+    "tab-size", "table-layout", "target", "target-name", "target-new",
+    "target-position", "text-align", "text-align-last", "text-decoration",
+    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+    "text-decoration-style", "text-emphasis", "text-emphasis-color",
+    "text-emphasis-position", "text-emphasis-style", "text-height",
+    "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
+    "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
+    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "transition", "transition-delay", "transition-duration",
+    "transition-property", "transition-timing-function", "unicode-bidi",
+    "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+    "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+    "word-spacing", "word-wrap", "z-index", "zoom",
+    // SVG-specific
+    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+    "color-interpolation", "color-interpolation-filters", "color-profile",
+    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+    "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
+  ]);
+
+  var colorKeywords = keySet([
+    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+    "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew",
+    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+    "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
+    "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+    "whitesmoke", "yellow", "yellowgreen"
+  ]);
+
+  var valueKeywords = keySet([
+    "above", "absolute", "activeborder", "activecaption", "afar",
+    "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
+    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+    "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
+    "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
+    "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+    "both", "bottom", "break-all", "break-word", "button", "button-bevel",
+    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
+    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+    "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
+    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+    "col-resize", "collapse", "compact", "condensed", "contain", "content",
+    "content-box", "context-menu", "continuous", "copy", "cover", "crop",
+    "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
+    "decimal-leading-zero", "default", "default-button", "destination-atop",
+    "destination-in", "destination-out", "destination-over", "devanagari",
+    "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
+    "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+    "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
+    "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
+    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
+    "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
+    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
+    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+    "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
+    "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
+    "landscape", "lao", "large", "larger", "left", "level", "lighter",
+    "line-through", "linear", "lines", "list-item", "listbox", "listitem",
+    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+    "lower-roman", "lowercase", "ltr", "malayalam", "match",
+    "media-controls-background", "media-current-time-display",
+    "media-fullscreen-button", "media-mute-button", "media-play-button",
+    "media-return-to-realtime-button", "media-rewind-button",
+    "media-seek-back-button", "media-seek-forward-button", "media-slider",
+    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+    "menu", "menulist", "menulist-button", "menulist-text",
+    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+    "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
+    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+    "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+    "outside", "overlay", "overline", "padding", "padding-box", "painted",
+    "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
+    "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
+    "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
+    "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
+    "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
+    "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
+    "searchfield-cancel-button", "searchfield-decoration",
+    "searchfield-results-button", "searchfield-results-decoration",
+    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+    "single", "skip-white-space", "slide", "slider-horizontal",
+    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+    "small", "small-caps", "small-caption", "smaller", "solid", "somali",
+    "source-atop", "source-in", "source-out", "source-over", "space", "square",
+    "square-button", "start", "static", "status-bar", "stretch", "stroke",
+    "sub", "subpixel-antialiased", "super", "sw-resize", "table",
+    "table-caption", "table-cell", "table-column", "table-column-group",
+    "table-footer-group", "table-header-group", "table-row", "table-row-group",
+    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+    "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+    "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
+    "xx-large", "xx-small"
+  ]);
+
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = null;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ["comment", "comment"];
+  }
+
+  CodeMirror.defineMIME("text/css", {
+    atMediaTypes: atMediaTypes,
+    atMediaFeatures: atMediaFeatures,
+    propertyKeywords: propertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    hooks: {
+      "<": function(stream, state) {
+        function tokenSGMLComment(stream, state) {
+          var dashes = 0, ch;
+          while ((ch = stream.next()) != null) {
+            if (dashes >= 2 && ch == ">") {
+              state.tokenize = null;
+              break;
+            }
+            dashes = (ch == "-") ? dashes + 1 : 0;
+          }
+          return ["comment", "comment"];
+        }
+        if (stream.eat("!")) {
+          state.tokenize = tokenSGMLComment;
+          return tokenSGMLComment(stream, state);
+        }
+      },
+      "/": function(stream, state) {
+        if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        }
+        return false;
+      }
+    },
+    name: "css-base"
+  });
+
+  CodeMirror.defineMIME("text/x-scss", {
+    atMediaTypes: atMediaTypes,
+    atMediaFeatures: atMediaFeatures,
+    propertyKeywords: propertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    allowNested: true,
+    hooks: {
+      "$": function(stream) {
+        stream.match(/^[\w-]+/);
+        if (stream.peek() == ":") {
+          return ["variable", "variable-definition"];
+        }
+        return ["variable", "variable"];
+      },
+      "/": function(stream, state) {
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return ["comment", "comment"];
+        } else if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return ["operator", "operator"];
+        }
+      },
+      "#": function(stream) {
+        if (stream.eat("{")) {
+          return ["operator", "interpolation"];
+        } else {
+          stream.eatWhile(/[\w\\\-]/);
+          return ["atom", "hash"];
+        }
+      }
+    },
+    name: "css-base"
+  });
+})();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/codemirror/htmlmixed.js
@@ -0,0 +1,104 @@
+CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
+  var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
+  var cssMode = CodeMirror.getMode(config, "css");
+
+  var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes;
+  scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i,
+                    mode: CodeMirror.getMode(config, "javascript")});
+  if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) {
+    var conf = scriptTypesConf[i];
+    scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)});
+  }
+  scriptTypes.push({matches: /./,
+                    mode: CodeMirror.getMode(config, "text/plain")});
+
+  function html(stream, state) {
+    var tagName = state.htmlState.tagName;
+    var style = htmlMode.token(stream, state.htmlState);
+    if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
+      // Script block: mode to change to depends on type attribute
+      var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i);
+      scriptType = scriptType ? scriptType[1] : "";
+      if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1);
+      for (var i = 0; i < scriptTypes.length; ++i) {
+        var tp = scriptTypes[i];
+        if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) {
+          if (tp.mode) {
+            state.token = script;
+            state.localMode = tp.mode;
+            state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, ""));
+          }
+          break;
+        }
+      }
+    } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") {
+      state.token = css;
+      state.localMode = cssMode;
+      state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
+    }
+    return style;
+  }
+  function maybeBackup(stream, pat, style) {
+    var cur = stream.current();
+    var close = cur.search(pat), m;
+    if (close > -1) stream.backUp(cur.length - close);
+    else if (m = cur.match(/<\/?$/)) {
+      stream.backUp(cur.length);
+      if (!stream.match(pat, false)) stream.match(cur[0]);
+    }
+    return style;
+  }
+  function script(stream, state) {
+    if (stream.match(/^<\/\s*script\s*>/i, false)) {
+      state.token = html;
+      state.localState = state.localMode = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*script\s*>/,
+                       state.localMode.token(stream, state.localState));
+  }
+  function css(stream, state) {
+    if (stream.match(/^<\/\s*style\s*>/i, false)) {
+      state.token = html;
+      state.localState = state.localMode = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*style\s*>/,
+                       cssMode.token(stream, state.localState));
+  }
+
+  return {
+    startState: function() {
+      var state = htmlMode.startState();
+      return {token: html, localMode: null, localState: null, htmlState: state};
+    },
+
+    copyState: function(state) {
+      if (state.localState)
+        var local = CodeMirror.copyState(state.localMode, state.localState);
+      return {token: state.token, localMode: state.localMode, localState: local,
+              htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+    },
+
+    token: function(stream, state) {
+      return state.token(stream, state);
+    },
+
+    indent: function(state, textAfter) {
+      if (!state.localMode || /^\s*<\//.test(textAfter))
+        return htmlMode.indent(state.htmlState, textAfter);
+      else if (state.localMode.indent)
+        return state.localMode.indent(state.localState, textAfter);
+      else
+        return CodeMirror.Pass;
+    },
+
+    electricChars: "/{}:",
+
+    innerMode: function(state) {
+      return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
+    }
+  };
+}, "xml", "javascript", "css");
+
+CodeMirror.defineMIME("text/html", "htmlmixed");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/codemirror/mozilla.css
@@ -0,0 +1,30 @@
+.breakpoints {
+  width: 16px;
+}
+
+.breakpoint, .debugLocation, .breakpoint-debugLocation {
+  display: inline-block;
+  margin-left: 5px;
+  width: 14px;
+  height: 14px;
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 12px;
+}
+
+.breakpoint {
+  background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
+}
+
+.debugLocation {
+  background-image: url("chrome://browser/skin/devtools/orion-debug-location.png");
+}
+
+.breakpoint.debugLocation {
+  background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
+    url("chrome://browser/skin/devtools/orion-breakpoint.png");
+}
+
+.CodeMirror-activeline-background {
+  background: #e8f2ff;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/codemirror/xml.js
@@ -0,0 +1,341 @@
+CodeMirror.defineMode("xml", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
+  var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true;
+
+  var Kludges = parserConfig.htmlMode ? {
+    autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+                      'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+                      'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+                      'track': true, 'wbr': true},
+    implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+                       'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+                       'th': true, 'tr': true},
+    contextGrabbers: {
+      'dd': {'dd': true, 'dt': true},
+      'dt': {'dd': true, 'dt': true},
+      'li': {'li': true},
+      'option': {'option': true, 'optgroup': true},
+      'optgroup': {'optgroup': true},
+      'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+            'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+            'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+            'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+            'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+      'rp': {'rp': true, 'rt': true},
+      'rt': {'rp': true, 'rt': true},
+      'tbody': {'tbody': true, 'tfoot': true},
+      'td': {'td': true, 'th': true},
+      'tfoot': {'tbody': true},
+      'th': {'td': true, 'th': true},
+      'thead': {'tbody': true, 'tfoot': true},
+      'tr': {'tr': true}
+    },
+    doNotIndent: {"pre": true},
+    allowUnquoted: true,
+    allowMissing: true
+  } : {
+    autoSelfClosers: {},
+    implicitlyClosed: {},
+    contextGrabbers: {},
+    doNotIndent: {},
+    allowUnquoted: false,
+    allowMissing: false
+  };
+  var alignCDATA = parserConfig.alignCDATA;
+
+  // Return variables for tokenizers
+  var tagName, type;
+
+  function inText(stream, state) {
+    function chain(parser) {
+      state.tokenize = parser;
+      return parser(stream, state);
+    }
+
+    var ch = stream.next();
+    if (ch == "<") {
+      if (stream.eat("!")) {
+        if (stream.eat("[")) {
+          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+          else return null;
+        } else if (stream.match("--")) {
+          return chain(inBlock("comment", "-->"));
+        } else if (stream.match("DOCTYPE", true, true)) {
+          stream.eatWhile(/[\w\._\-]/);
+          return chain(doctype(1));
+        } else {
+          return null;
+        }
+      } else if (stream.eat("?")) {
+        stream.eatWhile(/[\w\._\-]/);
+        state.tokenize = inBlock("meta", "?>");
+        return "meta";
+      } else {
+        var isClose = stream.eat("/");
+        tagName = "";
+        var c;
+        while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
+        if (!tagName) return "error";
+        type = isClose ? "closeTag" : "openTag";
+        state.tokenize = inTag;
+        return "tag";
+      }
+    } else if (ch == "&") {
+      var ok;
+      if (stream.eat("#")) {
+        if (stream.eat("x")) {
+          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+        } else {
+          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+        }
+      } else {
+        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+      }
+      return ok ? "atom" : "error";
+    } else {
+      stream.eatWhile(/[^&<]/);
+      return null;
+    }
+  }
+
+  function inTag(stream, state) {
+    var ch = stream.next();
+    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+      state.tokenize = inText;
+      type = ch == ">" ? "endTag" : "selfcloseTag";
+      return "tag";
+    } else if (ch == "=") {
+      type = "equals";
+      return null;
+    } else if (ch == "<") {
+      return "error";
+    } else if (/[\'\"]/.test(ch)) {
+      state.tokenize = inAttribute(ch);
+      state.stringStartCol = stream.column();
+      return state.tokenize(stream, state);
+    } else {
+      stream.eatWhile(/[^\s\u00a0=<>\"\']/);
+      return "word";
+    }
+  }
+
+  function inAttribute(quote) {
+    var closure = function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.next() == quote) {
+          state.tokenize = inTag;
+          break;
+        }
+      }
+      return "string";
+    };
+    closure.isInAttribute = true;
+    return closure;
+  }
+
+  function inBlock(style, terminator) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.match(terminator)) {
+          state.tokenize = inText;
+          break;
+        }
+        stream.next();
+      }
+      return style;
+    };
+  }
+  function doctype(depth) {
+    return function(stream, state) {
+      var ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == "<") {
+          state.tokenize = doctype(depth + 1);
+          return state.tokenize(stream, state);
+        } else if (ch == ">") {
+          if (depth == 1) {
+            state.tokenize = inText;
+            break;
+          } else {
+            state.tokenize = doctype(depth - 1);
+            return state.tokenize(stream, state);
+          }
+        }
+      }
+      return "meta";
+    };
+  }
+
+  var curState, curStream, setStyle;
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+
+  function pushContext(tagName, startOfLine) {
+    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
+    curState.context = {
+      prev: curState.context,
+      tagName: tagName,
+      indent: curState.indented,
+      startOfLine: startOfLine,
+      noIndent: noIndent
+    };
+  }
+  function popContext() {
+    if (curState.context) curState.context = curState.context.prev;
+  }
+
+  function element(type) {
+    if (type == "openTag") {
+      curState.tagName = tagName;
+      curState.tagStart = curStream.column();
+      return cont(attributes, endtag(curState.startOfLine));
+    } else if (type == "closeTag") {
+      var err = false;
+      if (curState.context) {
+        if (curState.context.tagName != tagName) {
+          if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
+            popContext();
+          }
+          err = !curState.context || curState.context.tagName != tagName;
+        }
+      } else {
+        err = true;
+      }
+      if (err) setStyle = "error";
+      return cont(endclosetag(err));
+    }
+    return cont();
+  }
+  function endtag(startOfLine) {
+    return function(type) {
+      var tagName = curState.tagName;
+      curState.tagName = curState.tagStart = null;
+      if (type == "selfcloseTag" ||
+          (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
+        maybePopContext(tagName.toLowerCase());
+        return cont();
+      }
+      if (type == "endTag") {
+        maybePopContext(tagName.toLowerCase());
+        pushContext(tagName, startOfLine);
+        return cont();
+      }
+      return cont();
+    };
+  }
+  function endclosetag(err) {
+    return function(type) {
+      if (err) setStyle = "error";
+      if (type == "endTag") { popContext(); return cont(); }
+      setStyle = "error";
+      return cont(arguments.callee);
+    };
+  }
+  function maybePopContext(nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!curState.context) {
+        return;
+      }
+      parentTagName = curState.context.tagName.toLowerCase();
+      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext();
+    }
+  }
+
+  function attributes(type) {
+    if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
+    if (type == "endTag" || type == "selfcloseTag") return pass();
+    setStyle = "error";
+    return cont(attributes);
+  }
+  function attribute(type) {
+    if (type == "equals") return cont(attvalue, attributes);
+    if (!Kludges.allowMissing) setStyle = "error";
+    else if (type == "word") setStyle = "attribute";
+    return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
+  }
+  function attvalue(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
+    setStyle = "error";
+    return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
+  }
+  function attvaluemaybe(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    else return pass();
+  }
+
+  return {
+    startState: function() {
+      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};
+    },
+
+    token: function(stream, state) {
+      if (!state.tagName && stream.sol()) {
+        state.startOfLine = true;
+        state.indented = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+
+      setStyle = type = tagName = null;
+      var style = state.tokenize(stream, state);
+      state.type = type;
+      if ((style || type) && style != "comment") {
+        curState = state; curStream = stream;
+        while (true) {
+          var comb = state.cc.pop() || element;
+          if (comb(type || style)) break;
+        }
+      }
+      state.startOfLine = false;
+      return setStyle || style;
+    },
+
+    indent: function(state, textAfter, fullLine) {
+      var context = state.context;
+      // Indent multi-line strings (e.g. css).
+      if (state.tokenize.isInAttribute) {
+        return state.stringStartCol + 1;
+      }
+      if ((state.tokenize != inTag && state.tokenize != inText) ||
+          context && context.noIndent)
+        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+      // Indent the starts of attribute names.
+      if (state.tagName) {
+        if (multilineTagIndentPastTag)
+          return state.tagStart + state.tagName.length + 2;
+        else
+          return state.tagStart + indentUnit * multilineTagIndentFactor;
+      }
+      if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+      if (context && /^<\//.test(textAfter))
+        context = context.prev;
+      while (context && !context.startOfLine)
+        context = context.prev;
+      if (context) return context.indent + indentUnit;
+      else return 0;
+    },
+
+    electricChars: "/",
+    blockCommentStart: "<!--",
+    blockCommentEnd: "-->",
+
+    configuration: parserConfig.htmlMode ? "html" : "xml",
+    helperType: parserConfig.htmlMode ? "html" : "xml"
+  };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/debugger.js
@@ -0,0 +1,267 @@
+/* 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 dbginfo = new WeakMap();
+
+// Private functions
+
+/**
+ * Adds a marker to the breakpoints gutter.
+ * Type should be either a 'breakpoint' or a 'debugLocation'.
+ */
+function addMarker(cm, line, type) {
+  let info = cm.lineInfo(line);
+
+  if (info.gutterMarkers)
+    return void info.gutterMarkers.breakpoints.classList.add(type);
+
+  let mark = cm.getWrapperElement().ownerDocument.createElement("div");
+  mark.className = type;
+  mark.innerHTML = "";
+
+  cm.setGutterMarker(info.line, "breakpoints", mark);
+}
+
+/**
+ * Removes a marker from the breakpoints gutter.
+ * Type should be either a 'breakpoint' or a 'debugLocation'.
+ */
+function removeMarker(cm, line, type) {
+  let info = cm.lineInfo(line);
+
+  if (!info || !info.gutterMarkers)
+    return;
+
+  info.gutterMarkers.breakpoints.classList.remove(type);
+}
+
+// These functions implement search within the debugger. Since
+// search in the debugger is different from other components,
+// we can't use search.js CodeMirror addon. This is a slightly
+// modified version of that addon. Depends on searchcursor.js.
+
+function SearchState() {
+  this.posFrom = this.posTo = this.query = null;
+}
+
+function getSearchState(cm) {
+  return cm.state.search || (cm.state.search = new SearchState());
+}
+
+function getSearchCursor(cm, query, pos) {
+  // If the query string is all lowercase, do a case insensitive search.
+  return cm.getSearchCursor(query, pos,
+    typeof query == "string" && query == query.toLowerCase());
+}
+
+/**
+ * If there's a saved search, selects the next results.
+ * Otherwise, creates a new search and selects the first
+ * result.
+ */
+function doSearch(cm, rev, query) {
+  let state = getSearchState(cm);
+
+  if (state.query)
+    return searchNext(cm, rev);
+
+  cm.operation(function () {
+    if (state.query) return;
+
+    state.query = query;
+    state.posFrom = state.posTo = { line: 0, ch: 0 };
+    searchNext(cm, rev);
+  });
+}
+
+/**
+ * Selects the next result of a saved search.
+ */
+function searchNext(cm, rev) {
+  cm.operation(function () {
+    let state = getSearchState(cm)
+    let cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+
+    if (!cursor.find(rev)) {
+      cursor = getSearchCursor(cm, state.query, rev ?
+        { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 });
+      if (!cursor.find(rev))
+        return;
+    }
+
+    cm.setSelection(cursor.from(), cursor.to());
+    state.posFrom = cursor.from();
+    state.posTo = cursor.to();
+  });
+}
+
+/**
+ * Clears the currently saved search.
+ */
+function clearSearch(cm) {
+  let state = getSearchState(cm);
+
+  if (!state.query)
+    return;
+
+  state.query = null;
+}
+
+// Exported functions
+
+/**
+ * This function is called whenever Editor is extended with functions
+ * from this module. See Editor.extend for more info.
+ */
+function initialize(ctx) {
+  let { ed } = ctx;
+
+  dbginfo.set(ed, {
+    breakpoints:   {},
+    debugLocation: null
+  });
+}
+
+/**
+ * True if editor has a visual breakpoint at that line, false
+ * otherwise.
+ */
+function hasBreakpoint(ctx, line) {
+  let { cm } = ctx;
+  let markers = cm.lineInfo(line).gutterMarkers;
+
+  return markers != null &&
+    markers.breakpoints.classList.contains("breakpoint");
+}
+
+/**
+ * Adds a visual breakpoint for a specified line. Third
+ * parameter 'cond' can hold any object.
+ *
+ * After adding a breakpoint, this function makes Editor to
+ * emit a breakpointAdded event.
+ */
+function addBreakpoint(ctx, line, cond) {
+  if (hasBreakpoint(ctx, line))
+    return;
+
+  let { ed, cm } = ctx;
+  let meta = dbginfo.get(ed);
+  let info = cm.lineInfo(line);
+
+  addMarker(cm, line, "breakpoint");
+  meta.breakpoints[line] = { condition: cond };
+
+  info.handle.on("delete", function onDelete() {
+    info.handle.off("delete", onDelete);
+    meta.breakpoints[info.line] = null;
+  });
+
+  ed.emit("breakpointAdded", line);
+}
+
+/**
+ * Removes a visual breakpoint from a specified line and
+ * makes Editor to emit a breakpointRemoved event.
+ */
+function removeBreakpoint(ctx, line) {
+  if (!hasBreakpoint(ctx, line))
+    return;
+
+  let { ed, cm } = ctx;
+  let meta = dbginfo.get(ed);
+  let info = cm.lineInfo(line);
+
+  meta.breakpoints[info.line] = null;
+  removeMarker(cm, info.line, "breakpoint");
+  ed.emit("breakpointRemoved", line);
+}
+
+/**
+ * Returns a list of all breakpoints in the current Editor.
+ */
+function getBreakpoints(ctx) {
+  let { ed } = ctx;
+  let meta = dbginfo.get(ed);
+
+  return Object.keys(meta.breakpoints).reduce((acc, line) => {
+    if (meta.breakpoints[line] != null)
+      acc.push({ line: line, condition: meta.breakpoints[line].condition });
+    return acc;
+  }, []);
+}
+
+/**
+ * Saves a debug location information and adds a visual anchor to
+ * the breakpoints gutter. This is used by the debugger UI to
+ * display the line on which the Debugger is currently paused.
+ */
+function setDebugLocation(ctx, line) {
+  let { ed, cm } = ctx;
+  let meta = dbginfo.get(ed);
+
+  meta.debugLocation = line;
+  addMarker(cm, line, "debugLocation");
+}
+
+/**
+ * Returns a line number that corresponds to the current debug
+ * location.
+ */
+function getDebugLocation(ctx) {
+  let { ed } = ctx;
+  let meta = dbginfo.get(ed);
+
+  return meta.debugLocation;
+}
+
+/**
+ * Clears the debug location. Clearing the debug location
+ * also removes a visual anchor from the breakpoints gutter.
+ */
+function clearDebugLocation(ctx) {
+  let { ed, cm } = ctx;
+  let meta = dbginfo.get(ed);
+
+  if (meta.debugLocation != null) {
+    removeMarker(cm, meta.debugLocation, "debugLocation");
+    meta.debugLocation = null;
+  }
+}
+
+/**
+ * Starts a new search.
+ */
+function find(ctx, query) {
+  let { cm } = ctx;
+  clearSearch(cm);
+  doSearch(cm, false, query);
+}
+
+/**
+ * Finds the next item based on the currently saved search.
+ */
+function findNext(ctx, query) {
+  let { cm } = ctx;
+  doSearch(cm, false, query);
+}
+
+/**
+ * Finds the previous item based on the currently saved search.
+ */
+function findPrev(ctx, query) {
+  let { cm } = ctx;
+  doSearch(cm, true, query);
+}
+
+
+// Export functions
+
+[
+  initialize, hasBreakpoint, addBreakpoint, removeBreakpoint,
+  getBreakpoints, setDebugLocation, getDebugLocation,
+  clearDebugLocation, find, findNext, findPrev
+].forEach(function (func) { module.exports[func.name] = func; });
\ No newline at end of file
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -19,26 +19,32 @@ Cu.import("resource://gre/modules/Servic
 const L10N = Services.strings.createBundle(L10N_BUNDLE);
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
 // order to initialize a CodeMirror instance.
 
 const CM_STYLES   = [
   "chrome://browser/content/devtools/codemirror/codemirror.css",
-  "chrome://browser/content/devtools/codemirror/dialog.css"
+  "chrome://browser/content/devtools/codemirror/dialog.css",
+  "chrome://browser/content/devtools/codemirror/mozilla.css"
 ];
 
 const CM_SCRIPTS  = [
   "chrome://browser/content/devtools/codemirror/codemirror.js",
   "chrome://browser/content/devtools/codemirror/dialog.js",
   "chrome://browser/content/devtools/codemirror/searchcursor.js",
   "chrome://browser/content/devtools/codemirror/search.js",
   "chrome://browser/content/devtools/codemirror/matchbrackets.js",
-  "chrome://browser/content/devtools/codemirror/comment.js"
+  "chrome://browser/content/devtools/codemirror/comment.js",
+  "chrome://browser/content/devtools/codemirror/javascript.js",
+  "chrome://browser/content/devtools/codemirror/xml.js",
+  "chrome://browser/content/devtools/codemirror/css.js",
+  "chrome://browser/content/devtools/codemirror/htmlmixed.js",
+  "chrome://browser/content/devtools/codemirror/activeline.js"
 ];
 
 const CM_IFRAME   =
   "data:text/html;charset=utf8,<!DOCTYPE html>" +
   "<html dir='ltr'>" +
   "  <head>" +
   "    <style>" +
   "      html, body { height: 100%; }" +
@@ -57,30 +63,33 @@ const CM_MAPPING = [
   "getCursor",
   "somethingSelected",
   "setSelection",
   "getSelection",
   "replaceSelection",
   "undo",
   "redo",
   "clearHistory",
-  "posFromIndex",
-  "openDialog"
+  "openDialog",
+  "cursorCoords",
+  "lineCount"
 ];
 
 const CM_JUMP_DIALOG = [
   L10N.GetStringFromName("gotoLineCmd.promptTitle")
     + " <input type=text style='width: 10em'/>"
 ];
 
 const editors = new WeakMap();
 
 Editor.modes = {
   text: { name: "text" },
-  js:   { name: "javascript", url: "chrome://browser/content/devtools/codemirror/javascript.js" }
+  js:   { name: "javascript" },
+  html: { name: "htmlmixed" },
+  css:  { name: "css" }
 };
 
 function ctrl(k) {
   return (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + k;
 }
 
 /**
  * A very thin wrapper around CodeMirror. Provides a number
@@ -104,24 +113,25 @@ function ctrl(k) {
  * CodeMirror docs: http://codemirror.net/doc/manual.html
  */
 function Editor(config) {
   const tabSize = Services.prefs.getIntPref(TAB_SIZE);
   const useTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
 
   this.version = null;
   this.config = {
-    value:          "",
-    mode:           Editor.modes.text,
-    indentUnit:     tabSize,
-    tabSize:        tabSize,
-    contextMenu:    null,
-    matchBrackets:  true,
-    extraKeys:      {},
-    indentWithTabs: useTabs,
+    value:           "",
+    mode:            Editor.modes.text,
+    indentUnit:      tabSize,
+    tabSize:         tabSize,
+    contextMenu:     null,
+    matchBrackets:   true,
+    extraKeys:       {},
+    indentWithTabs:  useTabs,
+    styleActiveLine: true
   };
 
   // Overwrite default config with user-provided, if needed.
   Object.keys(config).forEach((k) => this.config[k] = config[k]);
 
   // Additional shortcuts.
   this.config.extraKeys[ctrl("J")] = (cm) => this.jumpToLine();
   this.config.extraKeys[ctrl("/")] = "toggleComment";
@@ -146,18 +156,19 @@ function Editor(config) {
     if (cm.getCursor().ch !== 0) num -= 1;
     cm.replaceSelection(" ".repeat(num), "end", "+input");
   };
 
   events.decorate(this);
 }
 
 Editor.prototype = {
+  container: null,
   version: null,
-  config:  null,
+  config: null,
 
   /**
    * Appends the current Editor instance to the element specified by
    * the only argument 'el'. This method actually creates and loads
    * CodeMirror and all its dependencies.
    *
    * This method is asynchronous and returns a promise.
    */
@@ -169,40 +180,41 @@ Editor.prototype = {
     env.flex = 1;
 
     if (cm)
       throw new Error("You can append an editor only once.");
 
     let onLoad = () => {
       // Once the iframe is loaded, we can inject CodeMirror
       // and its dependencies into its DOM.
+
       env.removeEventListener("load", onLoad, true);
       let win = env.contentWindow.wrappedJSObject;
 
       CM_SCRIPTS.forEach((url) =>
         Services.scriptloader.loadSubScript(url, win, "utf8"));
 
-      // Plain text mode doesn't need any additional files,
-      // all other modes (js, html, etc.) do.
-      if (this.config.mode.name !== "text")
-        Services.scriptloader.loadSubScript(this.config.mode.url, win, "utf8");
-
       // Create a CodeMirror instance add support for context menus and
       // overwrite the default controller (otherwise items in the top and
       // context menus won't work).
 
       cm = win.CodeMirror(win.document.body, this.config);
       cm.getWrapperElement().addEventListener("contextmenu", (ev) => {
         ev.preventDefault();
+        this.emit("contextMenu");
         this.showContextMenu(doc, ev.screenX, ev.screenY);
       }, false);
 
       cm.on("change", () => this.emit("change"));
+      cm.on("gutterClick", (cm, line) => this.emit("gutterClick", line));
+      cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));
+
       doc.defaultView.controllers.insertControllerAt(0, controller(this, doc.defaultView));
 
+      this.container = env;
       editors.set(this, cm);
       def.resolve();
     };
 
     env.addEventListener("load", onLoad, true);
     env.setAttribute("src", CM_IFRAME);
     el.appendChild(env);
 
@@ -247,33 +259,61 @@ Editor.prototype = {
    */
   getOffset: function (...args) {
     let cm = editors.get(this);
     let res = args.map((pos) => cm.indexFromPos(pos));
     return args.length > 1 ? res : res[0];
   },
 
   /**
-   * Returns text from the text area.
+   * Returns text from the text area. If line argument is provided
+   * the method returns only that line.
    */
-  getText: function () {
+  getText: function (line) {
     let cm = editors.get(this);
-    return cm.getValue();
+    return line == null ?
+      cm.getValue() : (cm.lineInfo(line) ? cm.lineInfo(line).text : "");
   },
 
   /**
    * Replaces whatever is in the text area with the contents of
    * the 'value' argument.
    */
   setText: function (value) {
     let cm = editors.get(this);
     cm.setValue(value);
   },
 
   /**
+   * Changes the value of a currently used highlighting mode.
+   * See Editor.modes for the list of all suppoert modes.
+   */
+  setMode: function (value) {
+    let cm = editors.get(this);
+    cm.setOption("mode", value);
+  },
+
+  /**
+   * Returns the currently active highlighting mode.
+   * See Editor.modes for the list of all suppoert modes.
+   */
+  getMode: function () {
+    let cm = editors.get(this);
+    return cm.getOption("mode");
+  },
+
+  /**
+   * True if the editor is in the read-only mode, false otherwise.
+   */
+  isReadOnly: function () {
+    let cm = editors.get(this);
+    return cm.getOption("readOnly");
+  },
+
+  /**
    * Replaces contents of a text area within the from/to {line, ch}
    * range. If neither from nor to arguments are provided works
    * exactly like setText. If only from object is provided, inserts
    * text at that point.
    */
   replaceText: function (value, from, to) {
     let cm = editors.get(this);
 
@@ -335,18 +375,69 @@ Editor.prototype = {
    * This method opens an in-editor dialog asking for a line to
    * jump to. Once given, it changes cursor to that line.
    */
   jumpToLine: function () {
     this.openDialog(CM_JUMP_DIALOG, (line) =>
       this.setCursor({ line: line - 1, ch: 0 }));
   },
 
+  /**
+   * Returns a {line, ch} object that corresponds to the
+   * left, top coordinates.
+   */
+  getPositionFromCoords: function (left, top) {
+    let cm = editors.get(this);
+    return cm.coordsChar({ left: left, top: top });
+  },
+
+  /**
+   * Extends the current selection to the position specified
+   * by the provided {line, ch} object.
+   */
+  extendSelection: function (pos) {
+    let cm = editors.get(this);
+    let cursor = cm.indexFromPos(cm.getCursor());
+    let anchor = cm.posFromIndex(cursor + pos.start);
+    let head   = cm.posFromIndex(cursor + pos.start + pos.length);
+    cm.setSelection(anchor, head);
+  },
+
+  /**
+   * Extends an instance of the Editor object with additional
+   * functions. Each function will be called with context as
+   * the first argument. Context is a {ed, cm} object where
+   * 'ed' is an instance of the Editor object and 'cm' is an
+   * instance of the CodeMirror object. Example:
+   *
+   * function hello(ctx, name) {
+   *   let { cm, ed } = ctx;
+   *   cm;   // CodeMirror instance
+   *   ed;   // Editor instance
+   *   name; // 'Mozilla'
+   * }
+   *
+   * editor.extend({ hello: hello });
+   * editor.hello('Mozilla');
+   */
+  extend: function (funcs) {
+    Object.keys(funcs).forEach((name) => {
+      let cm  = editors.get(this);
+      let ctx = { ed: this, cm: cm };
+
+      if (name === "initialize")
+        return void funcs[name](ctx);
+
+      this[name] = funcs[name].bind(null, ctx);
+    });
+  },
+
   destroy: function () {
-    this.config  = null;
+    this.container = null;
+    this.config = null;
     this.version = null;
     this.emit("destroy");
   }
 };
 
 // Since Editor is a thin layer over CodeMirror some methods
 // are mapped directly—without any changes.
 
@@ -360,18 +451,16 @@ CM_MAPPING.forEach(function (name) {
 /**
  * Returns a controller object that can be used for
  * editor-specific commands such as find, jump to line,
  * copy/paste, etc.
  */
 function controller(ed, view) {
   return {
     supportsCommand: function (cmd) {
-      let cm = editors.get(ed);
-
       switch (cmd) {
         case "cmd_find":
         case "cmd_findAgain":
         case "cmd_findPrevious":
         case "cmd_gotoLine":
         case "cmd_undo":
         case "cmd_redo":
         case "cmd_cut":
--- a/browser/devtools/sourceeditor/moz.build
+++ b/browser/devtools/sourceeditor/moz.build
@@ -4,14 +4,15 @@
 # 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/.
 
 TEST_DIRS += ['test']
 
 JS_MODULES_PATH = 'modules/devtools/sourceeditor'
 
 EXTRA_JS_MODULES += [
+    'debugger.js',
     'editor.js',
     'source-editor-orion.jsm',
     'source-editor-ui.jsm',
     'source-editor.jsm',
 ]
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
@@ -96,17 +96,17 @@ function onSource(aEvent, aSource) {
 function checkCorrectLine(aCallback)
 {
   waitForSuccess({
     name: "correct source and line test for debugger for index " + index,
     validatorFn: function()
     {
       let debuggerView = dbg.panelWin.DebuggerView;
       if (debuggerView.editor &&
-          debuggerView.editor.getCaretPosition().line == line - 1) {
+          debuggerView.editor.getCursor().line == line - 1) {
         return true;
       }
       return false;
     },
     successFn: function()
     {
       aCallback && executeSoon(aCallback);
     },
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -349,16 +349,18 @@
 @BINPATH@/browser/components/nsBrowserContentHandler.js
 @BINPATH@/browser/components/nsBrowserGlue.js
 @BINPATH@/browser/components/nsSetDefaultBrowser.manifest
 @BINPATH@/browser/components/nsSetDefaultBrowser.js
 @BINPATH@/browser/components/BrowserDownloads.manifest
 @BINPATH@/browser/components/DownloadsStartup.js
 @BINPATH@/browser/components/DownloadsUI.js
 @BINPATH@/browser/components/BrowserPlaces.manifest
+@BINPATH@/browser/components/devtools-clhandler.manifest
+@BINPATH@/browser/components/devtools-clhandler.js
 @BINPATH@/components/Downloads.manifest
 @BINPATH@/components/DownloadLegacy.js
 @BINPATH@/components/BrowserPageThumbs.manifest
 @BINPATH@/components/SiteSpecificUserAgent.js
 @BINPATH@/components/SiteSpecificUserAgent.manifest
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -86,16 +86,28 @@
               this._fireEvent("select");
             } else {
               this._fireEvent("selectionchange");
             }
           ]]>
         </body>
       </method>
 
+      <method name="selectNone">
+        <body>
+          <![CDATA[
+            let selectedCount = this.selectedItems.length;
+            this.clearSelection();
+            if (selectedCount && "single" != this.getAttribute("seltype")) {
+              this._fireEvent("selectionchange");
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="handleItemClick">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
             if (!this.isBound)
               return;
 
@@ -111,17 +123,17 @@
         </body>
       </method>
 
       <method name="handleItemContextMenu">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            if (!this.isBound || this.suppressOnSelect)
+            if (!this.isBound || this.noContext)
               return;
             // we'll republish this as a selectionchange event on the grid
             aEvent.stopPropagation();
             this.toggleItemSelection(aItem);
           ]]>
         </body>
       </method>
 
@@ -187,17 +199,17 @@
           ]]>
         </getter>
         <setter>
           <![CDATA[
             if (val >= 0) {
               let selected = this.getItemAtIndex(val);
               this.selectItem(selected);
             } else {
-              this.clearSelection();
+              this.selectNone();
             }
           ]]>
         </setter>
       </property>
 
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
@@ -605,16 +617,19 @@
           ]]>
         </body>
       </method>
 
       <!-- Inteface to suppress selection events -->
       <property name="suppressOnSelect"
                   onget="return this.getAttribute('suppressonselect') == 'true';"
                   onset="this.setAttribute('suppressonselect', val);"/>
+      <property name="noContext"
+                  onget="return this.hasAttribute('nocontext');"
+                  onset="if (val) this.setAttribute('nocontext', true); else this.removeAttribute('nocontext');"/>
       <property name="crossSlideBoundary"
           onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
 
     <!-- Internal methods -->
       <field name="_xslideHandler"/>
       <constructor>
         <![CDATA[
           // create our quota of item slots
@@ -622,17 +637,17 @@
               count < slotCount; count++) {
             this.appendChild( this._createItemElement() );
           }
 
           if (this.controller && this.controller.gridBoundCallback != undefined)
             this.controller.gridBoundCallback();
 
           // set up cross-slide gesture handling for multiple-selection grids
-          if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
+          if ("undefined" !== typeof CrossSlide && !this.noContext) {
             this._xslideHandler = new CrossSlide.Handler(this, {
                   REARRANGESTART: this.crossSlideBoundary
             });
           }
 
           // XXX This event was never actually implemented (bug 223411).
           let event = document.createEvent("Events");
           event.initEvent("contentgenerated", true, true);
@@ -831,17 +846,17 @@
       <!--  /item bend effect handler -->
 
       <handler event="context-action">
         <![CDATA[
           // context-action is an event fired by the appbar typically
           // which directs us to do something to the selected tiles
           switch (event.action) {
             case "clear":
-              this.clearSelection();
+              this.selectNone();
               break;
             default:
               if (this.controller && this.controller.doActionOnSelectedTiles) {
                 this.controller.doActionOnSelectedTiles(event.action, event);
               }
           }
         ]]>
       </handler>
@@ -891,17 +906,17 @@
               event.target.removeAttribute('crosssliding');
               event.target.style.removeProperty('transform');
               break;
           }
         ]]>
       </handler>
       <handler event="MozCrossSlideSelect">
         <![CDATA[
-          if (this.suppressOnSelect)
+          if (this.noContext)
             return;
           this.toggleItemSelection(event.target);
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="richgrid-item">
--- a/browser/metro/base/content/bindings/urlbar.xml
+++ b/browser/metro/base/content/bindings/urlbar.xml
@@ -500,31 +500,30 @@
   </binding>
 
   <binding id="urlbar-autocomplete">
     <content class="meta-section-container">
       <xul:vbox class="meta-section" anonid="results-container" flex="1">
         <xul:label class="meta-section-title"
                    value="&autocompleteResultsHeader.label;"/>
         <richgrid anonid="results" rows="3" flex="1"
-                  seltype="single" deferlayout="true"/>
+                  seltype="single" nocontext="true" deferlayout="true"/>
       </xul:vbox>
 
       <xul:vbox class="meta-section" flex="1">
         <xul:label anonid="searches-header" class="meta-section-title"/>
         <richgrid anonid="searches" rows="3" flex="1"
-                  seltype="single" deferlayout="true"/>
+                  seltype="single" nocontext="true" deferlayout="true"/>
       </xul:vbox>
     </content>
 
     <implementation implements="nsIAutoCompletePopup, nsIObserver">
       <constructor>
         <![CDATA[
           this.hidden = true;
-
           Services.obs.addObserver(this, "browser-search-engine-modified", false);
 
           this._results.controller = this;
           this._searches.controller = this;
         ]]>
       </constructor>
 
       <destructor>
--- a/browser/metro/base/content/helperui/MenuUI.js
+++ b/browser/metro/base/content/helperui/MenuUI.js
@@ -373,23 +373,16 @@ MenuPopup.prototype = {
     }
 
     let width = this._popup.boxObject.width;
     let height = this._popup.boxObject.height;
     let halfWidth = width / 2;
     let screenWidth = ContentAreaObserver.width;
     let screenHeight = ContentAreaObserver.height;
 
-    // Add padding on the side of the menu per the user's hand preference
-    let leftHand =
-          Services.metro.handPreference == Ci.nsIWinMetroUtils.handPreferenceLeft;
-    if (aSource && aSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
-      this.commands.setAttribute("left-hand", leftHand);
-    }
-
     if (aPositionOptions.rightAligned)
       aX -= width;
 
     if (aPositionOptions.bottomAligned)
       aY -= height;
 
     if (aPositionOptions.centerHorizontally)
       aX -= halfWidth;
--- a/browser/metro/base/tests/mochitest/browser_snappedState.js
+++ b/browser/metro/base/tests/mochitest/browser_snappedState.js
@@ -109,25 +109,64 @@ gTests.push({
     ok(Browser.selectedBrowser.contentWindow.scrollMaxY !== 0, "Snapped scrolls vertically");
     ok(Browser.selectedBrowser.contentWindow.scrollMaxX === 0, "Snapped does not scroll horizontally");
   },
   tearDown: function() {
     BookmarksTestHelper.restore();
     yield restoreViewstate();
   }
 });
+gTests.push({
+  desc: "Test tile selection is cleared and disabled",
+  setUp: function() {
+    BookmarksTestHelper.setup();
+    HistoryTestHelper.setup();
+    showStartUI();
+  },
+  run: function() {
+    // minimal event mocking to trigger context-click handlers
+    function makeMockEvent(item) {
+      return {
+        stopPropagation: function() {},
+        target: item
+      };
+    }
+    let startWin = Browser.selectedBrowser.contentWindow;
+    // make sure the bookmarks grid is showing
+    startWin.StartUI.onNarrowTitleClick("start-bookmarks");
+    let bookmarksGrid = startWin.document.querySelector("#start-bookmarks-grid");
+    // sanity check
+    ok(bookmarksGrid, "matched bookmarks grid");
+    ok(bookmarksGrid.children[0], "bookmarks grid has items");
+    // select a tile (balancing implementation leakage with test simplicity)
+    let mockEvent = makeMockEvent(bookmarksGrid.children[0]);
+    bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent);
+    // check tile was selected
+    is(bookmarksGrid.selectedItems.length, 1, "Tile got selected in landscape view");
+    // switch to snapped view
+    yield setSnappedViewstate();
+    is(bookmarksGrid.selectedItems.length, 0, "grid items selection cleared in snapped view");
+    // attempt to select a tile in snapped view
+    mockEvent = makeMockEvent(bookmarksGrid.children[0]);
+    bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent);
+    is(bookmarksGrid.selectedItems.length, 0, "no grid item selections possible in snapped view");
+  },
+  tearDown: function() {
+    BookmarksTestHelper.restore();
+    HistoryTestHelper.restore();
+    yield restoreViewstate();
+  }
+});
 
 gTests.push({
   desc: "Navbar contextual buttons are not shown in snapped",
   setUp: setUpSnapped,
   run: function() {
     let toolbarContextual = document.getElementById("toolbar-contextual");
-
     let visibility = getComputedStyle(toolbarContextual).getPropertyValue("visibility");
-
     ok(visibility === "collapse" || visibility === "hidden", "Contextual buttons not shown in navbar");
   },
   tearDown: restoreViewstate
 });
 
 gTests.push({
   desc: "Test Portrait titles",
   setUp: setUpPortrait,
--- a/browser/metro/base/tests/mochitest/browser_tilegrid.xul
+++ b/browser/metro/base/tests/mochitest/browser_tilegrid.xul
@@ -3,31 +3,28 @@
 <!-- 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/. -->
 
 <?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
-
 <!DOCTYPE window []>
-
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
   <vbox id="alayout">
-      <richgrid id="grid_layout" seltype="single" flex="1">
+      <richgrid id="grid_layout" seltype="single" nocontext="true" flex="1">
       </richgrid>
   </vbox>
   <vbox>
       <richgrid id="slots_grid" seltype="single" minSlots="6" flex="1"/>
   </vbox>
   <vbox style="height:600px">
     <hbox>
-      <richgrid id="clearGrid" seltype="single" flex="1" rows="2">
+      <richgrid id="clearGrid" seltype="single" nocontext="true" flex="1" rows="2">
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
         <richgriditem value="about:blank" id="clearGrid_item2" label="2nd item"/>
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
       </richgrid>
     </hbox>
     <hbox>
       <richgrid id="emptyGrid" seltype="single" flex="1" rows="2" minSlots="6">
       </richgrid>
--- a/browser/metro/base/tests/mochitest/browser_tiles.js
+++ b/browser/metro/base/tests/mochitest/browser_tiles.js
@@ -350,33 +350,35 @@ gTests.push({
     is(grid.selectedIndex, -1, "selectedIndex reports correctly with nothing selected");
 
     // item selection
     grid.selectItem(grid.items[1]);
     ok(grid.items[1].selected, "Item selected property is truthy after grid.selectItem");
     ok(grid.items[1].getAttribute("selected"), "Item selected attribute is truthy after grid.selectItem");
     ok(grid.selectedItems.length, "There are selectedItems after grid.selectItem");
 
-    // clearSelection
-    grid.selectItem(grid.items[0]);
-    grid.selectItem(grid.items[1]);
-    grid.clearSelection();
-    is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
-    is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
-
     // select events
     // in seltype=single mode, select is like the default action for the tile
     // (think <a>, not <select multiple>)
     let handler = {
       handleEvent: function(aEvent) {}
     };
     let handlerStub = stubMethod(handler, "handleEvent");
+
+    grid.items[1].selected = true;
+
     doc.defaultView.addEventListener("select", handler, false);
     info("select listener added");
 
+    // clearSelection
+    grid.clearSelection();
+    is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
+    is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
+    is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
+
     info("calling selectItem, currently it is:" + grid.items[0].selected);
     // Note: A richgrid in seltype=single mode fires "select" events from selectItem
     grid.selectItem(grid.items[0]);
     info("calling selectItem, now it is:" + grid.items[0].selected);
     yield waitForMs(0);
 
     is(handlerStub.callCount, 1, "select event handler was called when we selected an item");
     is(handlerStub.calledWith[0].type, "select", "handler got a select event");
@@ -407,48 +409,82 @@ gTests.push({
     ok(grid.items[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item");
     is(grid.selectedItems.length, 1, "1 item selected when we first toggleItemSelection");
     is(grid.selectedItems[0], grid.items[1], "the right item is selected");
     is(grid.selectedIndex, 1, "selectedIndex is correct");
 
     grid.toggleItemSelection(grid.items[1]);
     is(grid.selectedItems.length, 0, "Nothing selected when we toggleItemSelection again");
 
-    // clearSelection
-    grid.items[0].selected=true;
-    grid.items[1].selected=true;
-    is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
-    grid.clearSelection();
-    is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
-    ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection");
-
     // selectionchange events
     // in seltype=multiple mode, we track selected state on all items
     // (think <select multiple> not <a>)
     let handler = {
       handleEvent: function(aEvent) {}
     };
     let handlerStub = stubMethod(handler, "handleEvent");
     doc.defaultView.addEventListener("selectionchange", handler, false);
     info("selectionchange listener added");
 
+    // clearSelection
+    grid.items[0].selected=true;
+    grid.items[1].selected=true;
+    is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
+    grid.clearSelection();
+    is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
+    ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection");
+    is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
+
     info("calling toggleItemSelection, currently it is:" + grid.items[0].selected);
     // Note: A richgrid in seltype=single mode fires "select" events from selectItem
     grid.toggleItemSelection(grid.items[0]);
     info("/calling toggleItemSelection, now it is:" + grid.items[0].selected);
     yield waitForMs(0);
 
     is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item");
     is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
     is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
     handlerStub.restore();
     doc.defaultView.removeEventListener("selectionchange", handler, false);
   }
 });
 
+gTests.push({
+  desc: "selectNone",
+  run: function() {
+    let grid = doc.querySelector("#grid-select2");
+
+    is(typeof grid.selectNone, "function", "selectNone is a function on the grid");
+
+    is(grid.itemCount, 2, "2 items initially");
+
+    // selectNone should fire a selectionchange event
+    let handler = {
+      handleEvent: function(aEvent) {}
+    };
+    let handlerStub = stubMethod(handler, "handleEvent");
+    doc.defaultView.addEventListener("selectionchange", handler, false);
+    info("selectionchange listener added");
+
+    grid.items[0].selected=true;
+    grid.items[1].selected=true;
+    is(grid.selectedItems.length, 2, "Both items are selected before calling selectNone");
+    grid.selectNone();
+
+    is(grid.selectedItems.length, 0, "Nothing selected when we selectNone");
+    ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we selectNone");
+
+    is(handlerStub.callCount, 1, "selectionchange event handler was called when we selectNone");
+    is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
+    is(handlerStub.calledWith[0].target, grid, "selectionchange event had the originating grid as the target");
+    handlerStub.restore();
+    doc.defaultView.removeEventListener("selectionchange", handler, false);
+  }
+});
+
 function gridSlotsSetup() {
     let grid = this.grid = doc.createElement("richgrid");
     grid.setAttribute("minSlots", 6);
     doc.documentElement.appendChild(grid);
     is(grid.ownerDocument, doc, "created grid in the expected document");
 }
 function gridSlotsTearDown() {
     this.grid && this.grid.parentNode.removeChild(this.grid);
--- a/browser/metro/modules/View.jsm
+++ b/browser/metro/modules/View.jsm
@@ -36,29 +36,38 @@ function View(aSet) {
 }
 
 View.prototype = {
   destruct: function () {
     Services.obs.removeObserver(this.viewStateObserver, "metro_viewstate_changed");
   },
 
   _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
-    if (this._set) {
-      if (undefined == aState)
-        aState = this._set.getAttribute("viewstate");
-
-      this._set.setAttribute("suppressonselect", (aState == "snapped"));
-
-      if (aState == "portrait") {
-        this._set.setAttribute("vertical", true);
-      } else {
-        this._set.removeAttribute("vertical");
-      }
-
-      this._set.arrangeItems();
+    let grid = this._set;
+    if (!grid) {
+      return;
+    }
+    if (!aState) {
+      aState = grid.getAttribute("viewstate");
+    }
+    switch (aState) {
+      case "snapped":
+        grid.setAttribute("nocontext", true);
+        grid.selectNone();
+        break;
+      case "portrait":
+        grid.removeAttribute("nocontext");
+        grid.setAttribute("vertical", true);
+        break;
+      default:
+        grid.removeAttribute("nocontext");
+        grid.removeAttribute("vertical");
+    }
+    if ("arrangeItems" in grid) {
+      grid.arrangeItems();
     }
   },
 
   _updateFavicon: function pv__updateFavicon(aItem, aUri) {
     if ("string" == typeof aUri) {
       aUri = makeURI(aUri);
     }
     PlacesUtils.favicons.getFaviconURLForPage(aUri, this._gotIcon.bind(this, aItem));
--- a/browser/metro/theme/platform.css
+++ b/browser/metro/theme/platform.css
@@ -202,21 +202,17 @@ menulist {
   color: black;
 }
 
 .menu-popup richlistitem:not([disabled]):active {
   background-color: black;
   color: white;
 }
 
-.menu-popup > richlistbox[left-hand="true"] > richlistitem {
-  padding-left: 50px;
-}
-
-.menu-popup > richlistbox[left-hand="false"] > richlistitem {
+.menu-popup > richlistbox > richlistitem {
   padding-right: 50px;
 }
 
 /* Additional styles applied to popups for form <select> elements. */
 
 #select-container {
   padding: 0;
   position: absolute;
--- a/browser/modules/SignInToWebsite.jsm
+++ b/browser/modules/SignInToWebsite.jsm
@@ -22,38 +22,27 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs));
 }
 
 this.SignInToWebsiteUX = {
 
   init: function SignInToWebsiteUX_init() {
 
-    /*
-     * bug 793906 - temporarily disabling desktop UI so we can
-     * focus on b2g without worrying about desktop as well
-     *
     Services.obs.addObserver(this, "identity-request", false);
     Services.obs.addObserver(this, "identity-auth", false);
     Services.obs.addObserver(this, "identity-auth-complete", false);
     Services.obs.addObserver(this, "identity-login-state-changed", false);
-     */
   },
 
   uninit: function SignInToWebsiteUX_uninit() {
-    /*
-     * As above:
-     * bug 793906 - temporarily disabling desktop UI so we can
-     * focus on b2g without worrying about desktop as well
-     *
     Services.obs.removeObserver(this, "identity-request");
     Services.obs.removeObserver(this, "identity-auth");
     Services.obs.removeObserver(this, "identity-auth-complete");
     Services.obs.removeObserver(this, "identity-login-state-changed");
-     */
   },
 
   observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
     log("observe: received", aTopic, "with", aData, "for", aSubject);
     let options = null;
     if (aSubject) {
       options = aSubject.wrappedJSObject;
     }
--- a/browser/modules/test/Makefile.in
+++ b/browser/modules/test/Makefile.in
@@ -2,11 +2,8 @@
 # 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/.
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 MOCHITEST_BROWSER_FILES += \
   browser_taskbar_preview.js \
   $(NULL)
 endif
-
-# bug 793906 - temporarily disabling desktop UI while working on b2g
-# browser_SignInToWebsite.js
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 
 [browser_NetworkPrioritizer.js]
+[browser_SignInToWebsite.js]
 [browser_UITour.js]
 support-files = uitour.*
\ No newline at end of file
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -3114,30 +3114,34 @@ Element::GetMarkup(bool aIncludeSelf, ns
   nsIDocument* doc = OwnerDoc();
   if (IsInHTMLDocument()) {
     Serialize(this, !aIncludeSelf, aMarkup);
     return;
   }
 
   nsAutoString contentType;
   doc->GetContentType(contentType);
+  bool tryToCacheEncoder = !aIncludeSelf;
 
   nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
   if (!docEncoder) {
     docEncoder =
       do_CreateInstance(PromiseFlatCString(
         nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
         NS_ConvertUTF16toUTF8(contentType)
       ).get());
   }
   if (!docEncoder) {
     // This could be some type for which we create a synthetic document.  Try
     // again as XML
     contentType.AssignLiteral("application/xml");
     docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
+    // Don't try to cache the encoder since it would point to a different
+    // contentType once it has been reinitialized.
+    tryToCacheEncoder = false;
   }
 
   NS_ENSURE_TRUE_VOID(docEncoder);
 
   uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
                    // Output DOM-standard newlines
                    nsIDocumentEncoder::OutputLFLineBreak |
                    // Don't do linebreaking that's not present in
@@ -3158,17 +3162,17 @@ Element::GetMarkup(bool aIncludeSelf, ns
 
   if (aIncludeSelf) {
     docEncoder->SetNativeNode(this);
   } else {
     docEncoder->SetNativeContainerNode(this);
   }
   rv = docEncoder->EncodeToString(aMarkup);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!aIncludeSelf) {
+  if (tryToCacheEncoder) {
     doc->SetCachedEncoder(docEncoder.forget());
   }
 }
 
 /**
  * Fire mutation events for changes caused by parsing directly into a
  * context node.
  *
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -73,16 +73,17 @@
 #include "GeckoProfiler.h"
 #include "nsObjectFrame.h"
 #include "nsDOMClassInfo.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMJSUtils.h"
 
 #include "nsWidgetsCID.h"
 #include "nsContentCID.h"
+#include "mozilla/BasicEvents.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Telemetry.h"
 
 #ifdef XP_WIN
 // Thanks so much, Microsoft! :(
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
--- a/content/events/src/nsContentEventHandler.cpp
+++ b/content/events/src/nsContentEventHandler.cpp
@@ -21,16 +21,17 @@
 #include "nsTextFrame.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIMEStateManager.h"
 #include "nsIObjectFrame.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/TextEvents.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /******************************************************************/
 /* nsContentEventHandler                                          */
 /******************************************************************/
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/dom/TabParent.h"
 
 #include "nsCOMPtr.h"
 #include "nsEventStateManager.h"
+#include "nsFocusManager.h"
 #include "nsIMEStateManager.h"
 #include "nsContentEventHandler.h"
 #include "nsIContent.h"
 #include "nsINodeInfo.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
@@ -1950,17 +1951,17 @@ nsEventStateManager::FireContextClick()
         if (frameSel && frameSel->GetMouseDownState()) {
           // note that this can cause selection changed events to fire if we're in
           // a text field, which will null out mCurrentTarget
           frameSel->SetMouseDownState(false);
         }
       }
 
       nsIDocument* doc = mGestureDownContent->GetCurrentDoc();
-      nsAutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc);
+      AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc);
 
       // dispatch to DOM
       nsEventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event,
                                   nullptr, &status);
 
       // We don't need to dispatch to frame handling because no frames
       // watch NS_CONTEXTMENU except for nsMenuFrame and that's only for
       // dismissal. That's just as well since we don't really know
@@ -5843,9 +5844,56 @@ nsEventStateManager::Prefs::GetAccessMod
       return sChromeAccessModifierMask;
     case nsIDocShellTreeItem::typeContent:
       return sContentAccessModifierMask;
     default:
       return 0;
   }
 }
 
-
+/******************************************************************/
+/* mozilla::AutoHandlingUserInputStatePusher                      */
+/******************************************************************/
+
+AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
+                                    bool aIsHandlingUserInput,
+                                    WidgetEvent* aEvent,
+                                    nsIDocument* aDocument) :
+  mIsHandlingUserInput(aIsHandlingUserInput),
+  mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN),
+  mResetFMMouseDownState(false)
+{
+  if (!aIsHandlingUserInput) {
+    return;
+  }
+  nsEventStateManager::StartHandlingUserInput();
+  if (!mIsMouseDown) {
+    return;
+  }
+  nsIPresShell::SetCapturingContent(nullptr, 0);
+  nsIPresShell::AllowMouseCapture(true);
+  if (!aDocument || !aEvent->mFlags.mIsTrusted) {
+    return;
+  }
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
+  NS_ENSURE_TRUE_VOID(fm);
+  fm->SetMouseButtonDownHandlingDocument(aDocument);
+  mResetFMMouseDownState = true;
+}
+
+AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher()
+{
+  if (!mIsHandlingUserInput) {
+    return;
+  }
+  nsEventStateManager::StopHandlingUserInput();
+  if (!mIsMouseDown) {
+    return;
+  }
+  nsIPresShell::AllowMouseCapture(false);
+  if (!mResetFMMouseDownState) {
+    return;
+  }
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
+  NS_ENSURE_TRUE_VOID(fm);
+  fm->SetMouseButtonDownHandlingDocument(nullptr);
+}
+
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -1,30 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsEventStateManager_h__
 #define nsEventStateManager_h__
 
-#include "mozilla/BasicEvents.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TypedEnum.h"
 
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsFocusManager.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIFrame.h"
 #include "Units.h"
 
+class nsFrameLoader;
 class nsIContent;
 class nsIDocument;
 class nsIDocShell;
 class nsIDocShellTreeNode;
 class nsIDocShellTreeItem;
 class imgIContainer;
 class nsDOMDataTransfer;
 class MouseEnterLeaveDispatcher;
@@ -842,73 +841,43 @@ public:
                             mozilla::WidgetGUIEvent* aMouseDownEvent);
   void KillClickHoldTimer();
   void FireContextClick();
 
   void SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) ;
   static void sClickHoldCallback ( nsITimer* aTimer, void* aESM ) ;
 };
 
+namespace mozilla {
+
 /**
  * This class is used while processing real user input. During this time, popups
  * are allowed. For mousedown events, mouse capturing is also permitted.
  */
-class nsAutoHandlingUserInputStatePusher
+class AutoHandlingUserInputStatePusher
 {
 public:
-  nsAutoHandlingUserInputStatePusher(bool aIsHandlingUserInput,
-                                     mozilla::WidgetEvent* aEvent,
-                                     nsIDocument* aDocument)
-    : mIsHandlingUserInput(aIsHandlingUserInput),
-      mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN),
-      mResetFMMouseDownState(false)
-  {
-    if (aIsHandlingUserInput) {
-      nsEventStateManager::StartHandlingUserInput();
-      if (mIsMouseDown) {
-        nsIPresShell::SetCapturingContent(nullptr, 0);
-        nsIPresShell::AllowMouseCapture(true);
-        if (aDocument && aEvent->mFlags.mIsTrusted) {
-          nsFocusManager* fm = nsFocusManager::GetFocusManager();
-          if (fm) {
-            fm->SetMouseButtonDownHandlingDocument(aDocument);
-            mResetFMMouseDownState = true;
-          }
-        }
-      }
-    }
-  }
-
-  ~nsAutoHandlingUserInputStatePusher()
-  {
-    if (mIsHandlingUserInput) {
-      nsEventStateManager::StopHandlingUserInput();
-      if (mIsMouseDown) {
-        nsIPresShell::AllowMouseCapture(false);
-        if (mResetFMMouseDownState) {
-          nsFocusManager* fm = nsFocusManager::GetFocusManager();
-          if (fm) {
-            fm->SetMouseButtonDownHandlingDocument(nullptr);
-          }
-        }
-      }
-    }
-  }
+  AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput,
+                                   WidgetEvent* aEvent,
+                                   nsIDocument* aDocument);
+  ~AutoHandlingUserInputStatePusher();
 
 protected:
   bool mIsHandlingUserInput;
   bool mIsMouseDown;
   bool mResetFMMouseDownState;
 
 private:
   // Hide so that this class can only be stack-allocated
   static void* operator new(size_t /*size*/) CPP_THROW_NEW { return nullptr; }
   static void operator delete(void* /*memory*/) {}
 };
 
+} // namespace mozilla
+
 // Click and double-click events need to be handled even for content that
 // has no frame. This is required for Web compatibility.
 #define NS_EVENT_NEEDS_FRAME(event) \
     (!(event)->HasPluginActivationEventMessage() && \
      (event)->message != NS_MOUSE_CLICK && \
      (event)->message != NS_MOUSE_DOUBLECLICK)
 
 #endif // nsEventStateManager_h__
--- a/content/events/src/nsPrivateTextRange.h
+++ b/content/events/src/nsPrivateTextRange.h
@@ -5,17 +5,17 @@
 
 #ifndef nsPrivateTextRange_h__
 #define nsPrivateTextRange_h__
 
 #include "nsIPrivateTextRange.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 
 class nsPrivateTextRange MOZ_FINAL : public nsIPrivateTextRange
 {
 	NS_DECL_ISUPPORTS
 public:
 
 	nsPrivateTextRange(const mozilla::TextRange &aTextRange);
 	virtual ~nsPrivateTextRange(void);
--- a/content/html/content/src/HTMLAudioElement.cpp
+++ b/content/html/content/src/HTMLAudioElement.cpp
@@ -12,33 +12,28 @@
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "AudioSampleFormat.h"
 #include "AudioChannelCommon.h"
 #include <algorithm>
-#include "mozilla/Preferences.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIHttpChannel.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "AudioStream.h"
 
-static bool
-IsAudioAPIEnabled()
-{
-  return mozilla::Preferences::GetBool("media.audio_data.enabled", true);
-}
-
 NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
 
 namespace mozilla {
 namespace dom {
 
+extern bool IsAudioAPIEnabled();
+
 NS_IMPL_ISUPPORTS_INHERITED4(HTMLAudioElement, HTMLMediaElement,
                              nsIDOMHTMLMediaElement, nsIDOMHTMLAudioElement,
                              nsITimerCallback, nsIAudioChannelAgentCallback)
 
 NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
 
 
 HTMLAudioElement::HTMLAudioElement(already_AddRefed<nsINodeInfo> aNodeInfo)
--- a/content/html/content/src/HTMLFormElement.cpp
+++ b/content/html/content/src/HTMLFormElement.cpp
@@ -802,19 +802,19 @@ HTMLFormElement::SubmitSubmission(nsForm
   //
   // Submit
   //
   nsCOMPtr<nsIDocShell> docShell;
 
   {
     nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState);
 
-    nsAutoHandlingUserInputStatePusher userInpStatePusher(
-                                         mSubmitInitiatedFromUserInput,
-                                         nullptr, doc);
+    AutoHandlingUserInputStatePusher userInpStatePusher(
+                                       mSubmitInitiatedFromUserInput,
+                                       nullptr, doc);
 
     nsCOMPtr<nsIInputStream> postDataStream;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -987,16 +987,22 @@ static bool IsAutoplayEnabled()
   return Preferences::GetBool("media.autoplay.enabled");
 }
 
 static bool UseAudioChannelService()
 {
   return Preferences::GetBool("media.useAudioChannelService");
 }
 
+// Not static because it's used in HTMLAudioElement.
+bool IsAudioAPIEnabled()
+{
+  return mozilla::Preferences::GetBool("media.audio_data.enabled", false);
+}
+
 void HTMLMediaElement::UpdatePreloadAction()
 {
   PreloadAction nextAction = PRELOAD_UNDEFINED;
   // If autoplay is set, or we're playing, we should always preload data,
   // as we'll need it to play.
   if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
       !mPaused)
   {
@@ -1532,16 +1538,21 @@ NS_IMETHODIMP HTMLMediaElement::SetVolum
   ErrorResult rv;
   SetVolume(aVolume, rv);
   return rv.ErrorCode();
 }
 
 uint32_t
 HTMLMediaElement::GetMozChannels(ErrorResult& aRv) const
 {
+  if (!IsAudioAPIEnabled()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return 0;
+  }
+
   if (!mDecoder && !mAudioStream) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return 0;
   }
 
   return mChannels;
 }
 
@@ -1551,16 +1562,21 @@ HTMLMediaElement::GetMozChannels(uint32_
   ErrorResult rv;
   *aMozChannels = GetMozChannels(rv);
  return rv.ErrorCode();
 }
 
 uint32_t
 HTMLMediaElement::GetMozSampleRate(ErrorResult& aRv) const
 {
+  if (!IsAudioAPIEnabled()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return 0;
+  }
+
   if (!mDecoder && !mAudioStream) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return 0;
   }
 
   return mRate;
 }
 
@@ -1642,16 +1658,21 @@ HTMLMediaElement::MozGetMetadata(JSConte
   }
 
   return rv.ErrorCode();
 }
 
 uint32_t
 HTMLMediaElement::GetMozFrameBufferLength(ErrorResult& aRv) const
 {
+  if (!IsAudioAPIEnabled()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return 0;
+  }
+
   // The framebuffer (via MozAudioAvailable events) is only available
   // when reading vs. writing audio directly.
   if (!mDecoder) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return 0;
   }
 
   return mDecoder->GetFrameBufferLength();
@@ -1663,16 +1684,21 @@ HTMLMediaElement::GetMozFrameBufferLengt
   ErrorResult rv;
   *aMozFrameBufferLength = GetMozFrameBufferLength(rv);
   return rv.ErrorCode();
 }
 
 void
 HTMLMediaElement::SetMozFrameBufferLength(uint32_t aMozFrameBufferLength, ErrorResult& aRv)
 {
+  if (!IsAudioAPIEnabled()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
   if (!mDecoder) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   aRv = mDecoder->RequestFrameBufferLength(aMozFrameBufferLength);
 }
 
@@ -3185,16 +3211,20 @@ nsresult HTMLMediaElement::DispatchAudio
                                                        float aTime)
 {
   // Auto manage the memory for the frame buffer. If we fail and return
   // an error, this ensures we free the memory in the frame buffer. Otherwise
   // we hand off ownership of the frame buffer to the audioavailable event,
   // which frees the memory when it's destroyed.
   nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
 
+  if (!IsAudioAPIEnabled()) {
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
   nsRefPtr<HTMLMediaElement> kungFuDeathGrip = this;
   NS_ENSURE_TRUE(domDoc, NS_ERROR_INVALID_ARG);
 
   nsCOMPtr<nsIDOMEvent> event;
   nsresult rv = domDoc->CreateEvent(NS_LITERAL_STRING("MozAudioAvailableEvent"),
                                     getter_AddRefs(event));
   nsCOMPtr<nsIDOMNotifyAudioAvailableEvent> audioavailableEvent(do_QueryInterface(event));
--- a/content/media/ogg/OggCodecState.cpp
+++ b/content/media/ogg/OggCodecState.cpp
@@ -57,17 +57,17 @@ static int64_t LEInt64(const unsigned ch
 
 // Reads a little-endian encoded unsigned 16bit integer at p.
 static uint16_t LEUint16(const unsigned char* p)
 {
   return p[0] + (p[1] << 8);
 }
 
 // Reads a little-endian encoded signed 16bit integer at p.
-static int16_t LEInt16(const unsigned char* p)
+inline int16_t LEInt16(const unsigned char* p)
 {
   return static_cast<int16_t>(LEUint16(p));
 }
 
 /** Decoder base class for Ogg-encapsulated streams. */
 OggCodecState*
 OggCodecState::Create(ogg_page* aPage)
 {
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -54,17 +54,19 @@ extern PRLogModuleInfo* gMediaDecoderLog
 // lands between the seek target and SEEK_FUZZ_USECS microseconds before the
 // seek target.  This is becaue it's usually quicker to just keep downloading
 // from an exisiting connection than to do another bisection inside that
 // small range, which would open a new HTTP connetion.
 static const uint32_t SEEK_FUZZ_USECS = 500000;
 
 // The number of microseconds of "pre-roll" we use for Opus streams.
 // The specification recommends 80 ms.
+#ifdef MOZ_OPUS
 static const int64_t SEEK_OPUS_PREROLL = 80 * USECS_PER_MS;
+#endif /* MOZ_OPUS */
 
 enum PageSyncResult {
   PAGE_SYNC_ERROR = 1,
   PAGE_SYNC_END_OF_RANGE= 2,
   PAGE_SYNC_OK = 3
 };
 
 // Reads a page from the media resource.
@@ -81,18 +83,20 @@ PageSync(MediaResource* aResource,
 // is about 4300 bytes, so we read the file in chunks larger than that.
 static const int PAGE_STEP = 8192;
 
 OggReader::OggReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
     mMonitor("OggReader"),
     mTheoraState(nullptr),
     mVorbisState(nullptr),
+#ifdef MOZ_OPUS
     mOpusState(nullptr),
     mOpusEnabled(MediaDecoder::IsOpusEnabled()),
+#endif /* MOZ_OPUS */
     mSkeletonState(nullptr),
     mVorbisSerial(0),
     mOpusSerial(0),
     mTheoraSerial(0),
     mOpusPreSkip(0),
     mIsChained(false),
     mDecodedAudioFrames(0)
 {
@@ -126,19 +130,21 @@ nsresult OggReader::ResetDecode(bool sta
     res = NS_ERROR_FAILURE;
   }
 
   // Discard any previously buffered packets/pages.
   ogg_sync_reset(&mOggState);
   if (mVorbisState && NS_FAILED(mVorbisState->Reset())) {
     res = NS_ERROR_FAILURE;
   }
+#ifdef MOZ_OPUS
   if (mOpusState && NS_FAILED(mOpusState->Reset(start))) {
     res = NS_ERROR_FAILURE;
   }
+#endif /* MOZ_OPUS */
   if (mTheoraState && NS_FAILED(mTheoraState->Reset())) {
     res = NS_ERROR_FAILURE;
   }
 
   return res;
 }
 
 bool OggReader::ReadHeaders(OggCodecState* aState)
@@ -157,18 +163,20 @@ bool OggReader::ReadHeaders(OggCodecStat
 void OggReader::BuildSerialList(nsTArray<uint32_t>& aTracks)
 {
   if (HasVideo()) {
     aTracks.AppendElement(mTheoraState->mSerial);
   }
   if (HasAudio()) {
     if (mVorbisState) {
       aTracks.AppendElement(mVorbisState->mSerial);
-    } else if(mOpusState) {
+#ifdef MOZ_OPUS
+    } else if (mOpusState) {
       aTracks.AppendElement(mOpusState->mSerial);
+#endif /* MOZ_OPUS */
     }
   }
 }
 
 nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
                                  MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
@@ -215,27 +223,29 @@ nsresult OggReader::ReadMetadata(MediaIn
       if (codecState &&
           codecState->GetType() == OggCodecState::TYPE_THEORA &&
           !mTheoraState)
       {
         // First Theora bitstream, we'll play this one. Subsequent Theora
         // bitstreams will be ignored.
         mTheoraState = static_cast<TheoraState*>(codecState);
       }
+#ifdef MOZ_OPUS
       if (codecState &&
           codecState->GetType() == OggCodecState::TYPE_OPUS &&
           !mOpusState)
       {
         if (mOpusEnabled) {
           mOpusState = static_cast<OpusState*>(codecState);
         } else {
           NS_WARNING("Opus decoding disabled."
                      " See media.opus.enabled in about:config");
         }
       }
+#endif /* MOZ_OPUS */
       if (codecState &&
           codecState->GetType() == OggCodecState::TYPE_SKELETON &&
           !mSkeletonState)
       {
         mSkeletonState = static_cast<SkeletonState*>(codecState);
       }
     }
 
@@ -249,17 +259,20 @@ nsresult OggReader::ReadMetadata(MediaIn
 
   // We've read all BOS pages, so we know the streams contained in the media.
   // Now process all available header packets in the active Theora, Vorbis and
   // Skeleton streams.
 
   // Deactivate any non-primary bitstreams.
   for (uint32_t i = 0; i < bitstreams.Length(); i++) {
     OggCodecState* s = bitstreams[i];
-    if (s != mVorbisState && s != mOpusState &&
+    if (s != mVorbisState &&
+#ifdef MOZ_OPUS
+        s != mOpusState &&
+#endif /* MOZ_OPUS */
         s != mTheoraState && s != mSkeletonState) {
       s->Deactivate();
     }
   }
 
   if (mTheoraState && ReadHeaders(mTheoraState)) {
     nsIntRect picture = nsIntRect(mTheoraState->mInfo.pic_x,
                                   mTheoraState->mInfo.pic_y,
@@ -600,26 +613,32 @@ void OggReader::DownmixToStereo(nsAutoAr
 #endif
   channels = out_channels;
   buffer = dBuffer;
 }
 
 bool OggReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  NS_ASSERTION(mVorbisState != nullptr || mOpusState != nullptr,
-    "Need audio codec state to decode audio");
+  DebugOnly<bool> haveCodecState = mVorbisState != nullptr
+#ifdef MOZ_OPUS
+    || mOpusState != nullptr
+#endif /* MOZ_OPUS */
+    ;
+  NS_ASSERTION(haveCodecState, "Need audio codec state to decode audio");
 
   // Read the next data packet. Skip any non-data packets we encounter.
   ogg_packet* packet = 0;
   OggCodecState* codecState;
   if (mVorbisState)
     codecState = static_cast<OggCodecState*>(mVorbisState);
+#ifdef MOZ_OPUS
   else
     codecState = static_cast<OggCodecState*>(mOpusState);
+#endif /* MOZ_OPUS */
   do {
     if (packet) {
       OggCodecState::ReleasePacket(packet);
     }
     packet = NextOggPacket(codecState);
   } while (packet && codecState->IsHeader(packet));
 
   if (!packet) {
@@ -656,17 +675,19 @@ void OggReader::SetChained(bool aIsChain
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaSeekable(false);
   }
 }
 
 bool OggReader::ReadOggChain()
 {
   bool chained = false;
+#ifdef MOZ_OPUS
   OpusState* newOpusState = nullptr;
+#endif /* MOZ_OPUS */
   VorbisState* newVorbisState = nullptr;
   int channels = 0;
   long rate = 0;
   MetadataTags* tags = nullptr;
 
   if (HasVideo() || HasSkeleton() || !HasAudio()) {
     return false;
   }
@@ -1335,20 +1356,22 @@ nsresult OggReader::SeekInUnbuffered(int
   // don't do this offsetting when seeking in a buffered range,
   // as the extra decoding causes a noticeable speed hit when all the data
   // is buffered (compared to just doing a bisection to exactly find the
   // keyframe).
   int64_t keyframeOffsetMs = 0;
   if (HasVideo() && mTheoraState) {
     keyframeOffsetMs = mTheoraState->MaxKeyframeOffset();
   }
+#ifdef MOZ_OPUS
   // Add in the Opus pre-roll if necessary, as well.
   if (HasAudio() && mOpusState) {
     keyframeOffsetMs = std::max(keyframeOffsetMs, SEEK_OPUS_PREROLL);
   }
+#endif /* MOZ_OPUS */
   int64_t seekTarget = std::max(aStartTime, aTarget - keyframeOffsetMs);
   // Minimize the bisection search space using the known timestamps from the
   // buffered ranges.
   SeekRange k = SelectSeekRange(aRanges, seekTarget, aStartTime, aEndTime, false);
   return SeekBisection(seekTarget, k, SEEK_FUZZ_USECS);
 }
 
 nsresult OggReader::Seek(int64_t aTarget,
@@ -1359,19 +1382,21 @@ nsresult OggReader::Seek(int64_t aTarget
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   if (mIsChained)
     return NS_ERROR_FAILURE;
   LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget));
   nsresult res;
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource != nullptr, NS_ERROR_FAILURE);
   int64_t adjustedTarget = aTarget;
+#ifdef MOZ_OPUS
   if (HasAudio() && mOpusState){
     adjustedTarget = std::max(aStartTime, aTarget - SEEK_OPUS_PREROLL);
   }
+#endif /* MOZ_OPUS */
 
   if (adjustedTarget == aStartTime) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
     res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
     res = ResetDecode(true);
@@ -1817,20 +1842,22 @@ nsresult OggReader::GetBuffered(TimeRang
         continue;
       }
 
       uint32_t serial = ogg_page_serialno(&page);
       if (mVorbisState && serial == mVorbisSerial) {
         startTime = VorbisState::Time(&mVorbisInfo, granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       }
+#ifdef MOZ_OPUS
       else if (mOpusState && serial == mOpusSerial) {
         startTime = OpusState::Time(mOpusPreSkip, granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       }
+#endif /* MOZ_OPUS */
       else if (mTheoraState && serial == mTheoraSerial) {
         startTime = TheoraState::Time(&mTheoraInfo, granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       }
       else if (mCodecStore.Contains(serial)) {
         // Stream is not the theora or vorbis stream we're playing,
         // but is one that we have header data for.
         startOffset += page.header_len + page.body_len;
--- a/content/media/ogg/OggReader.h
+++ b/content/media/ogg/OggReader.h
@@ -57,18 +57,21 @@ public:
 
   // If the Theora granulepos has not been captured, it may read several packets
   // until one with a granulepos has been captured, to ensure that all packets
   // read have valid time info.
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
                                   int64_t aTimeThreshold);
 
   virtual bool HasAudio() {
-    return (mVorbisState != 0 && mVorbisState->mActive) ||
-           (mOpusState != 0 && mOpusState->mActive);
+    return (mVorbisState != 0 && mVorbisState->mActive)
+#ifdef MOZ_OPUS
+      || (mOpusState != 0 && mOpusState->mActive)
+#endif /* MOZ_OPUS */
+      ;
   }
 
   virtual bool HasVideo() {
     return mTheoraState != 0 && mTheoraState->mActive;
   }
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
@@ -263,23 +266,25 @@ private:
   OggCodecStore mCodecStore;
 
   // Decode state of the Theora bitstream we're decoding, if we have video.
   TheoraState* mTheoraState;
 
   // Decode state of the Vorbis bitstream we're decoding, if we have audio.
   VorbisState* mVorbisState;
 
+#ifdef MOZ_OPUS
   // Decode state of the Opus bitstream we're decoding, if we have one.
   OpusState *mOpusState;
 
   // Represents the user pref media.opus.enabled at the time our
   // contructor was called. We can't check it dynamically because
   // we're not on the main thread;
   bool mOpusEnabled;
+#endif /* MOZ_OPUS */
 
   // Decode state of the Skeleton bitstream.
   SkeletonState* mSkeletonState;
 
   // Ogg decoding state.
   ogg_sync_state mOggState;
 
   // Vorbis/Opus/Theora data used to compute timestamps. This is written on the
--- a/content/media/test/test_a4_tone.html
+++ b/content/media/test/test_a4_tone.html
@@ -10,19 +10,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Media test: simple audioAvalailable event checks</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
 
-<!-- mute audio, since there is no need to hear the sound for these tests -->
-<audio id='a1' controls></audio>
-
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /**
 * FFT is a class for calculating the Discrete Fourier Transform of a signal
 * with the Fast Fourier Transform algorithm.
 *
 * Source: github.com/corbanbrook/dsp.js; License: MIT; Copyright: Corban Brook
@@ -238,27 +235,29 @@ function checkResults() {
   SimpleTest.finish();
 }
 
 function audioEnded() {
   checkResults();
 }
 
 function initTest() {
-  var a1 = document.getElementById('a1');
+  var a1 = document.createElement("audio");
+  a1.id = "a1";
   a1.addEventListener("ended", audioEnded, false);
   a1.addEventListener("loadedmetadata", loadedMetadata, false);
   a1.addEventListener("MozAudioAvailable", audioAvailable, false);
   a1.src = testFile;
   a1.muted = true;
   a1.play();
+  document.body.appendChild(a1);
 }
 
 window.addEventListener("load", function(e) {
-  initTest();
+  SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, initTest);
 }, false);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_audio_event_adopt.html
+++ b/content/media/test/test_audio_event_adopt.html
@@ -19,19 +19,21 @@ https://bugzilla.mozilla.org/show_bug.cg
     return document.adoptNode(resultNode).checked;
   }
   function endTest() {
     is(wasAudioAvailableCalled(), true, "audioAvailable was not called");
 
     SimpleTest.finish();
   }
   function startTest() {
-    var audio = adopt();
-    audio.addEventListener("ended", endTest, false);
-    audio.play();
+    SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, function () {
+      var audio = adopt();
+      audio.addEventListener("ended", endTest, false);
+      audio.play();
+    });
   }
 
   SimpleTest.waitForExplicitFinish();
 </script>
 
 </head>
 <body>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
--- a/content/media/test/test_audiowrite.html
+++ b/content/media/test/test_audiowrite.html
@@ -63,17 +63,17 @@ function runTests() {
   } catch(e) {
     writeArgsOK = true;
   }
   ok(writeArgsOK, "mozWriteAudio args test failed.");
   SimpleTest.finish();
 }
 
 window.addEventListener("load", function(e) {
-  runTests();
+  SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, runTests);
 }, false);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_bug686137.html
+++ b/content/media/test/test_bug686137.html
@@ -7,37 +7,41 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Media test: changing mozFrameBufferLength</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686137">Mozilla Bug 686137</a>
 
-<audio id="a1" controls muted preload="metadata"></audio>
-
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var testFile = "bug495794.ogg";
-var a1 = document.getElementById('a1');
+
+SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, function () {
+  var a1 = document.createElement("audio");
+  a1.controls = true;
+  a1.muted = true;
+  a1.preload = "metadata";
+  a1.addEventListener("loadedmetadata", metaDataLoaded, false);
+  a1.src = "bug495794.ogg";
+  a1.load();
+});
+SimpleTest.waitForExplicitFinish();
 
 function audioAvailable(event) {
+  var a1 = event.target;
   a1.removeEventListener("MozAudioAvailable", audioAvailable);
   is( event.frameBuffer.length, 9001, "event.frameBuffer.length should be 9001.");
   is( event.frameBuffer.length, a1.mozFrameBufferLength, "event.frameBuffer.length should be " + a1.mozFrameBufferLength + ".");
   SimpleTest.finish();
 }
 
-function metaDataLoaded(){
+function metaDataLoaded(event){
+  var a1 = event.target;
   a1.addEventListener("MozAudioAvailable", audioAvailable, false);
   a1.mozFrameBufferLength = 9001;
   a1.play();
 }
 
-a1.addEventListener("loadedmetadata", metaDataLoaded, false);
-a1.src = testFile;
-a1.load();
-SimpleTest.waitForExplicitFinish();
-
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_framebuffer.html
+++ b/content/media/test/test_framebuffer.html
@@ -7,19 +7,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Media test: framebuffer size checks</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
 
-<!-- mute audio, since there is no need to hear the sound for these tests -->
-<audio id="a1" preload="metadata" controls></audio>
-
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var testFile = "bug495794.ogg";
 var testFileDuration = 0.30;
 var testFileChannelCount = 2;
 var testFileSampleRate = 48000;
 var testFileFrameBufferLength = testFileChannelCount * 1024;
 
@@ -79,27 +76,32 @@ function checkResults() {
   SimpleTest.finish();
 }
 
 function audioEnded() {
   checkResults();
 }
 
 function initTest() {
-  var a1 = document.getElementById('a1');
+  var a1 = document.createElement("audio");
+  a1.id = "a1";
+  a1.preload = "metadata";
+  a1.controls = true;
+  document.body.appendChild(a1);
+
   a1.addEventListener("ended", audioEnded, false);
   a1.addEventListener("loadedmetadata", loadedMetadata, false);
   a1.addEventListener("MozAudioAvailable", audioAvailable, false);
   a1.src = testFile;
   a1.muted = true;
   a1.play();
 }
 
 window.addEventListener("load", function(e) {
-  initTest();
+  SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, initTest);
 }, false);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_wave_data_s16.html
+++ b/content/media/test/test_wave_data_s16.html
@@ -29,19 +29,22 @@ function audioavailable(e) {
 
   // Only care about the first few samples
   SimpleTest.finish();
 }
 
 function startTest() {
   if (completed)
     return;
-  var v = document.getElementById('v');
-  v.addEventListener('MozAudioAvailable', audioavailable, false);
-  v.play();
+  SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]},
+                            function () {
+    var v = document.getElementById('v');
+    v.addEventListener('MozAudioAvailable', audioavailable, false);
+    v.play();
+  });
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 <audio id='v'
        preload="metadata"
        onloadedmetadata='return startTest();'>
--- a/content/media/test/test_wave_data_u8.html
+++ b/content/media/test/test_wave_data_u8.html
@@ -29,19 +29,22 @@ function audioavailable(e) {
 
   // Only care about the first few samples
   SimpleTest.finish();
 }
 
 function startTest() {
   if (completed)
     return;
-  var v = document.getElementById('v');
-  v.addEventListener('MozAudioAvailable', audioavailable, false);
-  v.play();
+  SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]},
+                            function () {
+    var v = document.getElementById('v');
+    v.addEventListener('MozAudioAvailable', audioavailable, false);
+    v.play();
+  });
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 <audio id='v'
        preload="metadata"
        onloadedmetadata='return startTest();'>
--- a/content/media/webaudio/test/mochitest.ini
+++ b/content/media/webaudio/test/mochitest.ini
@@ -106,8 +106,9 @@ support-files =
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
 [test_singleSourceDest.html]
 [test_waveShaper.html]
 [test_waveShaperNoCurve.html]
 [test_waveShaperZeroLengthCurve.html]
 [test_audioDestinationNode.html]
 [test_mozaudiochannel.html]
+[test_waveDecoder.html]
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_waveDecoder.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset=utf-8>
+<head>
+  <title>Test that we decode uint8 and sint16 wave files with correct conversion to float64</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var testsDone = 0;
+var tests = ["UklGRjUrAABXQVZFZm10IBAAAAABAAEAESsAABErAAABAAgAZGF0YQMAAAD/AIA=",
+             "UklGRkZWAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQYAAAD/fwCAAAA="];
+
+SimpleTest.waitForExplicitFinish();
+
+function base64ToUint8Buffer(b64) {
+  var str = atob(b64)
+  var u8 = new Uint8Array(str.length);
+  for (var i = 0; i < str.length; ++i) {
+    u8[i] = str.charCodeAt(i);
+  }
+  return u8;
+}
+
+function fixupBufferSampleRate(u8, rate) {
+  u8[24] = (rate & 0x000000ff) >> 0;
+  u8[25] = (rate & 0x0000ff00) >> 8;
+  u8[26] = (rate & 0x00ff0000) >> 16;
+  u8[27] = (rate & 0xff000000) >> 24;
+}
+
+function finishTest() {
+  testsDone += 1;
+  if (testsDone == tests.length) {
+    SimpleTest.finish();
+  }
+}
+
+function decodeComplete(b) {
+  ok(true, "Decoding succeeded.");
+  is(b.numberOfChannels, 1, "Should have 1 channel.");
+  is(b.length, 3, "Should have three samples.");
+  var samples = b.getChannelData(0);
+  ok(samples[0] >  0.99 && samples[0] <  1.01, "Check near  1.0. Got " + samples[0]);
+  ok(samples[1] > -1.01 && samples[1] < -0.99, "Check near -1.0. Got " + samples[1]);
+  ok(samples[2] > -0.01 && samples[2] <  0.01, "Check near  0.0. Got " + samples[2]);
+  finishTest();
+}
+
+function decodeFailed() {
+  ok(false, "Decoding failed.");
+  finishTest();
+}
+
+addLoadEvent(function() {
+  var context = new AudioContext();
+
+  for (var i = 0; i < tests.length; ++i) {
+    var u8 = base64ToUint8Buffer(tests[i]);
+    fixupBufferSampleRate(u8, context.sampleRate);
+    context.decodeAudioData(u8.buffer, decodeComplete, decodeFailed);
+  }
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/apps/src/InterAppComm.cpp
+++ b/dom/apps/src/InterAppComm.cpp
@@ -7,17 +7,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsJSPrincipals.h"
 #include "mozilla/Preferences.h"
 #include "AccessCheck.h"
 
 using namespace mozilla::dom;
 
 /* static */ bool
-InterAppComm::EnabledForScope(JSContext* /* unused */, JSObject* aObj)
+InterAppComm::EnabledForScope(JSContext* /* unused */, JS::Handle<JSObject*> aObj)
 {
   // Disable the constructors if they're disabled by the preference for sure.
   if (!Preferences::GetBool("dom.inter-app-communication-api.enabled", false)) {
   	return false;
   }
 
   // Only expose the constructors to the chrome codes for Gecko internal uses.
   // The content pages shouldn't be aware of the constructors.
--- a/dom/apps/src/InterAppComm.h
+++ b/dom/apps/src/InterAppComm.h
@@ -12,15 +12,15 @@ struct JSContext;
 class JSObject;
 
 namespace mozilla {
 namespace dom {
 
 class InterAppComm
 {
 public:
-  static bool EnabledForScope(JSContext* /* unused */, JSObject* aObj);
+  static bool EnabledForScope(JSContext* /* unused */, JS::Handle<JSObject*> aObj);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_apps_InterAppComm_h
--- a/dom/base/CompositionStringSynthesizer.h
+++ b/dom/base/CompositionStringSynthesizer.h
@@ -6,17 +6,17 @@
 #ifndef mozilla_dom_compositionstringsynthesizer_h__
 #define mozilla_dom_compositionstringsynthesizer_h__
 
 #include "nsICompositionStringSynthesizer.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWeakReference.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 
 class nsIWidget;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class CompositionStringSynthesizer MOZ_FINAL :
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "amIAddonManager.h"
 #include "nsWindowMemoryReporter.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
+#include "nsIDOMWindowCollection.h"
 #include "nsIEffectiveTLDService.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsNetCID.h"
 #include "nsPrintfCString.h"
 #include "XPCJSMemoryReporter.h"
@@ -26,24 +27,80 @@ StaticRefPtr<nsWindowMemoryReporter> sWi
 nsWindowMemoryReporter::nsWindowMemoryReporter()
   : mCheckForGhostWindowsCallbackPending(false)
 {
 }
 
 NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
                    nsSupportsWeakReference)
 
-/* static */
-void
+static nsresult
+AddNonJSSizeOfWindowAndItsDescendents(nsGlobalWindow* aWindow,
+                                      nsTabSizes* aSizes)
+{
+  // Measure the window.
+  nsWindowSizes windowSizes(moz_malloc_size_of);
+  aWindow->AddSizeOfIncludingThis(&windowSizes);
+  windowSizes.addToTabSizes(aSizes);
+
+  // Measure the inner window, if there is one.
+  nsWindowSizes innerWindowSizes(moz_malloc_size_of);
+  nsGlobalWindow* inner = aWindow->GetCurrentInnerWindowInternal();
+  if (inner) {
+    inner->AddSizeOfIncludingThis(&innerWindowSizes);
+    innerWindowSizes.addToTabSizes(aSizes);
+  }
+
+  nsCOMPtr<nsIDOMWindowCollection> frames;
+  nsresult rv = aWindow->GetFrames(getter_AddRefs(frames));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t length;
+  rv = frames->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Measure this window's descendents.
+  for (uint32_t i = 0; i < length; i++) {
+      nsCOMPtr<nsIDOMWindow> child;
+      rv = frames->Item(i, getter_AddRefs(child));
+      NS_ENSURE_SUCCESS(rv, rv);
+      NS_ENSURE_STATE(child);
+
+      nsGlobalWindow* childWin =
+        static_cast<nsGlobalWindow*>(static_cast<nsIDOMWindow *>(child.get()));
+
+      rv = AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes);
+      NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+static nsresult
+NonJSSizeOfTab(nsPIDOMWindow* aWindow, size_t* aDomSize, size_t* aStyleSize, size_t* aOtherSize)
+{
+  nsGlobalWindow* window = static_cast<nsGlobalWindow*>(aWindow);
+
+  nsTabSizes sizes;
+  nsresult rv = AddNonJSSizeOfWindowAndItsDescendents(window, &sizes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aDomSize   = sizes.mDom;
+  *aStyleSize = sizes.mStyle;
+  *aOtherSize = sizes.mOther;
+  return NS_OK;
+}
+
+/* static */ void
 nsWindowMemoryReporter::Init()
 {
   MOZ_ASSERT(!sWindowReporter);
   sWindowReporter = new nsWindowMemoryReporter();
   ClearOnShutdown(&sWindowReporter);
   NS_RegisterMemoryReporter(sWindowReporter);
+  RegisterNonJSSizeOfTab(NonJSSizeOfTab);
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
     // when a window's docshell is set to NULL.
     os->AddObserver(sWindowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
     os->AddObserver(sWindowReporter, "after-minimize-memory-usage",
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -7,48 +7,69 @@
 #ifndef nsWindowMemoryReporter_h__
 #define nsWindowMemoryReporter_h__
 
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
 #include "nsDataHashtable.h"
 #include "nsWeakReference.h"
 #include "nsAutoPtr.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 #include "nsArenaMemoryStats.h"
-#include "mozilla/Attributes.h"
 
 // This should be used for any nsINode sub-class that has fields of its own
 // that it needs to measure;  any sub-class that doesn't use it will inherit
 // SizeOfExcludingThis from its super-class.  SizeOfIncludingThis() need not be
 // defined, it is inherited from nsINode.
 #define NS_DECL_SIZEOF_EXCLUDING_THIS \
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 class nsWindowSizes {
+#define FOR_EACH_SIZE(macro) \
+  macro(DOM,   mDOMElementNodes) \
+  macro(DOM,   mDOMTextNodes) \
+  macro(DOM,   mDOMCDATANodes) \
+  macro(DOM,   mDOMCommentNodes) \
+  macro(DOM,   mDOMEventTargets) \
+  macro(DOM,   mDOMOther) \
+  macro(Style, mStyleSheets) \
+  macro(Other, mLayoutPresShell) \
+  macro(Style, mLayoutStyleSets) \
+  macro(Other, mLayoutTextRuns) \
+  macro(Other, mLayoutPresContext) \
+  macro(Other, mPropertyTables) \
+
 public:
-  nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf) {
-    memset(this, 0, sizeof(nsWindowSizes));
-    mMallocSizeOf = aMallocSizeOf;
+  nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf)
+    :
+      #define ZERO_SIZE(kind, mSize)  mSize(0),
+      FOR_EACH_SIZE(ZERO_SIZE)
+      #undef ZERO_SIZE
+      mArenaStats(),
+      mMallocSizeOf(aMallocSizeOf)
+  {}
+
+  void addToTabSizes(nsTabSizes *sizes) const {
+    #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
+    FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+    #undef ADD_TO_TAB_SIZES
+    mArenaStats.addToTabSizes(sizes);
   }
-  mozilla::MallocSizeOf mMallocSizeOf;
+
+  #define DECL_SIZE(kind, mSize) size_t mSize;
+  FOR_EACH_SIZE(DECL_SIZE);
+  #undef DECL_SIZE
   nsArenaMemoryStats mArenaStats;
-  size_t mDOMElementNodes;
-  size_t mDOMTextNodes;
-  size_t mDOMCDATANodes;
-  size_t mDOMCommentNodes;
-  size_t mDOMEventTargets;
-  size_t mDOMOther;
-  size_t mStyleSheets;
-  size_t mLayoutPresShell;
-  size_t mLayoutStyleSets;
-  size_t mLayoutTextRuns;
-  size_t mLayoutPresContext;
-  size_t mPropertyTables;
+  mozilla::MallocSizeOf mMallocSizeOf;
+
+#undef FOR_EACH_SIZE
 };
 
 /**
  * nsWindowMemoryReporter is responsible for the 'explicit/window-objects'
  * memory reporter.
  *
  * We classify DOM window objects into one of three categories:
  *
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -2,20 +2,20 @@
 /* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_tabs_TabParent_h
 #define mozilla_tabs_TabParent_h
 
+#include "mozilla/EventForwards.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContentDialogParent.h"
 #include "mozilla/dom/TabContext.h"
-#include "mozilla/TouchEvents.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDialogParamBlock.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "Units.h"
 #include "js/TypeDecls.h"
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -109,21 +109,21 @@ partial interface HTMLMediaElement {
   readonly attribute boolean mozAudioCaptured;
 
   // Mozilla extension: extra stream metadata information, used as part
   // of MozAudioAvailable events and the mozWriteAudio() method.  The
   // mozFrameBufferLength method allows for the size of the framebuffer
   // used within MozAudioAvailable events to be changed.  The new size must
   // be between 512 and 16384.  The default size, for a  media element with
   // audio is (mozChannels * 1024).
-  [GetterThrows]
+  [Pref="media.audio_data.enabled", GetterThrows]
   readonly attribute unsigned long mozChannels;
-  [GetterThrows]
+  [Pref="media.audio_data.enabled", GetterThrows]
   readonly attribute unsigned long mozSampleRate;
-  [Throws]
+  [Pref="media.audio_data.enabled", Throws]
            attribute unsigned long mozFrameBufferLength;
 
   // Mozilla extension: return embedded metadata from the stream as a
   // JSObject with key:value pairs for each tag. This can be used by
   // player interfaces to display the song title, artist, etc.
   [Throws]
   object? mozGetMetadata();
 
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <string.h>
 
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/Base64.h"
+#include "mozilla/BasicEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Selection.h"
 #include "mozilla/Util.h"
 #include "nsAString.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -20,16 +20,47 @@
 #include "jspubtd.h"
 
 #include "js/HashTable.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 
 class nsISupports;      // Needed for ObjectPrivateVisitor.
 
+namespace JS {
+
+struct TabSizes
+{
+    enum Kind {
+        Objects,
+        Strings,
+        Private,
+        Other
+    };
+
+    TabSizes() { mozilla::PodZero(this); }
+
+    void add(Kind kind, size_t n) {
+        switch (kind) {
+            case Objects: objects  += n; break;
+            case Strings: strings  += n; break;
+            case Private: private_ += n; break;
+            case Other:   other    += n; break;
+            default:      MOZ_CRASH("bad TabSizes kind");
+        }
+    }
+
+    size_t objects;
+    size_t strings;
+    size_t private_;
+    size_t other;
+};
+
+} // namespace JS
+
 namespace js {
 
 // In memory reporting, we have concept of "sundries", line items which are too
 // small to be worth reporting individually.  Under some circumstances, a memory
 // reporter gets tossed into the sundries bucket if it's smaller than
 // MemoryReportingSundriesThreshold() bytes.
 //
 // We need to define this value here, rather than in the code which actually
@@ -52,42 +83,43 @@ struct InefficientNonFlatteningStringHas
 // without updating all the required methods.  So we define a single macro list
 // in each class to name the fields (and notable characteristics of them), and
 // then use the following macros to transform those lists into the required
 // methods.
 //
 // In some classes, one or more of the macro arguments aren't used.  We use '_'
 // for those.
 //
-#define DECL_SIZE(gc, mSize)                      size_t mSize;
-#define ZERO_SIZE(gc, mSize)                      mSize(0),
-#define COPY_OTHER_SIZE(gc, mSize)                mSize(other.mSize),
-#define ADD_OTHER_SIZE(gc, mSize)                 mSize += other.mSize;
-#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(gc, mSize) n += (gc) ? mSize : 0;
+#define DECL_SIZE(kind, gc, mSize)                      size_t mSize;
+#define ZERO_SIZE(kind, gc, mSize)                      mSize(0),
+#define COPY_OTHER_SIZE(kind, gc, mSize)                mSize(other.mSize),
+#define ADD_OTHER_SIZE(kind, gc, mSize)                 mSize += other.mSize;
+#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc == js::IsLiveGCThing) ? mSize : 0;
+#define ADD_TO_TAB_SIZES(kind, gc, mSize)               sizes->add(JS::TabSizes::kind, mSize);
 
 // Used to annotate which size_t fields measure live GC things and which don't.
 enum {
     NotLiveGCThing = false,
     IsLiveGCThing = true
 };
 
 struct ZoneStatsPod
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(NotLiveGCThing, gcHeapArenaAdmin) \
-    macro(NotLiveGCThing, unusedGCThings) \
-    macro(IsLiveGCThing,  lazyScriptsGCHeap) \
-    macro(NotLiveGCThing, lazyScriptsMallocHeap) \
-    macro(IsLiveGCThing,  ionCodesGCHeap) \
-    macro(IsLiveGCThing,  typeObjectsGCHeap) \
-    macro(NotLiveGCThing, typeObjectsMallocHeap) \
-    macro(NotLiveGCThing, typePool) \
-    macro(IsLiveGCThing,  stringsShortGCHeap) \
-    macro(IsLiveGCThing,  stringsNormalGCHeap) \
-    macro(NotLiveGCThing, stringsNormalMallocHeap)
+    macro(Other,   NotLiveGCThing, gcHeapArenaAdmin) \
+    macro(Other,   NotLiveGCThing, unusedGCThings) \
+    macro(Other,   IsLiveGCThing,  lazyScriptsGCHeap) \
+    macro(Other,   NotLiveGCThing, lazyScriptsMallocHeap) \
+    macro(Other,   IsLiveGCThing,  ionCodesGCHeap) \
+    macro(Other,   IsLiveGCThing,  typeObjectsGCHeap) \
+    macro(Other,   NotLiveGCThing, typeObjectsMallocHeap) \
+    macro(Other,   NotLiveGCThing, typePool) \
+    macro(Strings, IsLiveGCThing,  stringsShortGCHeap) \
+    macro(Strings, IsLiveGCThing,  stringsNormalGCHeap) \
+    macro(Strings, NotLiveGCThing, stringsNormalMallocHeap)
 
     ZoneStatsPod()
       : FOR_EACH_SIZE(ZERO_SIZE)
         extra()
     {}
 
     void add(const ZoneStatsPod &other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
@@ -96,71 +128,80 @@ struct ZoneStatsPod
 
     size_t sizeOfLiveGCThings() const {
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
         // Do nothing with |extra|.
         return n;
     }
 
+    void addToTabSizes(JS::TabSizes *sizes) const {
+        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+        // Do nothing with |extra|.
+    }
+
     FOR_EACH_SIZE(DECL_SIZE)
     void *extra;    // This field can be used by embedders.
 
 #undef FOR_EACH_SIZE
 };
 
 } // namespace js
 
 namespace JS {
 
 // Data for tracking memory usage of things hanging off objects.
 struct ObjectsExtraSizes
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(js::NotLiveGCThing, mallocHeapSlots) \
-    macro(js::NotLiveGCThing, mallocHeapElementsNonAsmJS) \
-    macro(js::NotLiveGCThing, mallocHeapElementsAsmJS) \
-    macro(js::NotLiveGCThing, nonHeapElementsAsmJS) \
-    macro(js::NotLiveGCThing, nonHeapCodeAsmJS) \
-    macro(js::NotLiveGCThing, mallocHeapAsmJSModuleData) \
-    macro(js::NotLiveGCThing, mallocHeapArgumentsData) \
-    macro(js::NotLiveGCThing, mallocHeapRegExpStatics) \
-    macro(js::NotLiveGCThing, mallocHeapPropertyIteratorData) \
-    macro(js::NotLiveGCThing, mallocHeapCtypesData)
+    macro(Objects, NotLiveGCThing, mallocHeapSlots) \
+    macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \
+    macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \
+    macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \
+    macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \
+    macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \
+    macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \
+    macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \
+    macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \
+    macro(Objects, NotLiveGCThing, mallocHeapCtypesData)
 
     ObjectsExtraSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         dummy()
     {}
 
     void add(const ObjectsExtraSizes &other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
     }
 
     size_t sizeOfLiveGCThings() const {
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
         return n;
     }
 
+    void addToTabSizes(TabSizes *sizes) const {
+        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+    }
+
     FOR_EACH_SIZE(DECL_SIZE)
     int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
 
 #undef FOR_EACH_SIZE
 };
 
 // Data for tracking JIT-code memory usage.
 struct CodeSizes
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(_, ion) \
-    macro(_, baseline) \
-    macro(_, regexp) \
-    macro(_, other) \
-    macro(_, unused)
+    macro(_, _, ion) \
+    macro(_, _, baseline) \
+    macro(_, _, regexp) \
+    macro(_, _, other) \
+    macro(_, _, unused)
 
     CodeSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         dummy()
     {}
 
     FOR_EACH_SIZE(DECL_SIZE)
     int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@@ -251,27 +292,27 @@ struct NotableStringInfo : public String
     char *buffer;
 };
 
 // These measurements relate directly to the JSRuntime, and not to zones and
 // compartments within it.
 struct RuntimeSizes
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(_, object) \
-    macro(_, atomsTable) \
-    macro(_, contexts) \
-    macro(_, dtoa) \
-    macro(_, temporary) \
-    macro(_, regexpData) \
-    macro(_, interpreterStack) \
-    macro(_, gcMarker) \
-    macro(_, mathCache) \
-    macro(_, scriptData) \
-    macro(_, scriptSources)
+    macro(_, _, object) \
+    macro(_, _, atomsTable) \
+    macro(_, _, contexts) \
+    macro(_, _, dtoa) \
+    macro(_, _, temporary) \
+    macro(_, _, regexpData) \
+    macro(_, _, interpreterStack) \
+    macro(_, _, gcMarker) \
+    macro(_, _, mathCache) \
+    macro(_, _, scriptData) \
+    macro(_, _, scriptSources)
 
     RuntimeSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         code()
     {}
 
     FOR_EACH_SIZE(DECL_SIZE)
     CodeSizes code;
@@ -330,45 +371,45 @@ struct ZoneStats : js::ZoneStatsPod
 
     StringsHashMap strings;
     js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
 };
 
 struct CompartmentStats
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(js::IsLiveGCThing,  objectsGCHeapOrdinary) \
-    macro(js::IsLiveGCThing,  objectsGCHeapFunction) \
-    macro(js::IsLiveGCThing,  objectsGCHeapDenseArray) \
-    macro(js::IsLiveGCThing,  objectsGCHeapSlowArray) \
-    macro(js::IsLiveGCThing,  objectsGCHeapCrossCompartmentWrapper) \
-    macro(js::NotLiveGCThing, objectsPrivate) \
-    macro(js::IsLiveGCThing,  shapesGCHeapTreeGlobalParented) \
-    macro(js::IsLiveGCThing,  shapesGCHeapTreeNonGlobalParented) \
-    macro(js::IsLiveGCThing,  shapesGCHeapDict) \
-    macro(js::IsLiveGCThing,  shapesGCHeapBase) \
-    macro(js::NotLiveGCThing, shapesMallocHeapTreeTables) \
-    macro(js::NotLiveGCThing, shapesMallocHeapDictTables) \
-    macro(js::NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
-    macro(js::NotLiveGCThing, shapesMallocHeapCompartmentTables) \
-    macro(js::IsLiveGCThing,  scriptsGCHeap) \
-    macro(js::NotLiveGCThing, scriptsMallocHeapData) \
-    macro(js::NotLiveGCThing, baselineData) \
-    macro(js::NotLiveGCThing, baselineStubsFallback) \
-    macro(js::NotLiveGCThing, baselineStubsOptimized) \
-    macro(js::NotLiveGCThing, ionData) \
-    macro(js::NotLiveGCThing, typeInferenceTypeScripts) \
-    macro(js::NotLiveGCThing, typeInferencePendingArrays) \
-    macro(js::NotLiveGCThing, typeInferenceAllocationSiteTables) \
-    macro(js::NotLiveGCThing, typeInferenceArrayTypeTables) \
-    macro(js::NotLiveGCThing, typeInferenceObjectTypeTables) \
-    macro(js::NotLiveGCThing, compartmentObject) \
-    macro(js::NotLiveGCThing, crossCompartmentWrappersTable) \
-    macro(js::NotLiveGCThing, regexpCompartment) \
-    macro(js::NotLiveGCThing, debuggeesSet)
+    macro(Objects, IsLiveGCThing,  objectsGCHeapOrdinary) \
+    macro(Objects, IsLiveGCThing,  objectsGCHeapFunction) \
+    macro(Objects, IsLiveGCThing,  objectsGCHeapDenseArray) \
+    macro(Objects, IsLiveGCThing,  objectsGCHeapSlowArray) \
+    macro(Objects, IsLiveGCThing,  objectsGCHeapCrossCompartmentWrapper) \
+    macro(Private, NotLiveGCThing, objectsPrivate) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapTreeGlobalParented) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapTreeNonGlobalParented) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapDict) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapBase) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeTables) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapDictTables) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapCompartmentTables) \
+    macro(Other,   IsLiveGCThing,  scriptsGCHeap) \
+    macro(Other,   NotLiveGCThing, scriptsMallocHeapData) \
+    macro(Other,   NotLiveGCThing, baselineData) \
+    macro(Other,   NotLiveGCThing, baselineStubsFallback) \
+    macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
+    macro(Other,   NotLiveGCThing, ionData) \
+    macro(Other,   NotLiveGCThing, typeInferenceTypeScripts) \
+    macro(Other,   NotLiveGCThing, typeInferencePendingArrays) \
+    macro(Other,   NotLiveGCThing, typeInferenceAllocationSiteTables) \
+    macro(Other,   NotLiveGCThing, typeInferenceArrayTypeTables) \
+    macro(Other,   NotLiveGCThing, typeInferenceObjectTypeTables) \
+    macro(Other,   NotLiveGCThing, compartmentObject) \
+    macro(Other,   NotLiveGCThing, crossCompartmentWrappersTable) \
+    macro(Other,   NotLiveGCThing, regexpCompartment) \
+    macro(Other,   NotLiveGCThing, debuggeesSet)
 
     CompartmentStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         objectsExtra(),
         extra()
     {}
 
     CompartmentStats(const CompartmentStats &other)
@@ -386,32 +427,38 @@ struct CompartmentStats
     size_t sizeOfLiveGCThings() const {
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
         n += objectsExtra.sizeOfLiveGCThings();
         // Do nothing with |extra|.
         return n;
     }
 
+    void addToTabSizes(TabSizes *sizes) const {
+        FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
+        objectsExtra.addToTabSizes(sizes);
+        // Do nothing with |extra|.
+    }
+
     FOR_EACH_SIZE(DECL_SIZE)
     ObjectsExtraSizes  objectsExtra;
     void               *extra;  // This field can be used by embedders.
 
 #undef FOR_EACH_SIZE
 };
 
 struct RuntimeStats
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(_, gcHeapChunkTotal) \
-    macro(_, gcHeapDecommittedArenas) \
-    macro(_, gcHeapUnusedChunks) \
-    macro(_, gcHeapUnusedArenas) \
-    macro(_, gcHeapChunkAdmin) \
-    macro(_, gcHeapGCThings) \
+    macro(_, _, gcHeapChunkTotal) \
+    macro(_, _, gcHeapDecommittedArenas) \
+    macro(_, _, gcHeapUnusedChunks) \
+    macro(_, _, gcHeapUnusedArenas) \
+    macro(_, _, gcHeapChunkAdmin) \
+    macro(_, _, gcHeapGCThings) \
 
     RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
       : FOR_EACH_SIZE(ZERO_SIZE)
         runtime(),
         cTotals(),
         zTotals(),
         compartmentStatsVector(),
         zoneStatsVector(),
@@ -483,11 +530,22 @@ extern JS_PUBLIC_API(size_t)
 SystemCompartmentCount(JSRuntime *rt);
 
 extern JS_PUBLIC_API(size_t)
 UserCompartmentCount(JSRuntime *rt);
 
 extern JS_PUBLIC_API(size_t)
 PeakSizeOfTemporary(const JSRuntime *rt);
 
+extern JS_PUBLIC_API(bool)
+AddSizeOfTab(JSRuntime *rt, JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
+             ObjectPrivateVisitor *opv, TabSizes *sizes);
+
 } // namespace JS
 
+#undef DECL_SIZE
+#undef ZERO_SIZE
+#undef COPY_OTHER_SIZE
+#undef ADD_OTHER_SIZE
+#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
+#undef ADD_TO_TAB_SIZES
+
 #endif /* js_MemoryMetrics_h */
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -21,43 +21,66 @@ void
 js::TraceRuntime(JSTracer *trc)
 {
     JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
 
     AutoPrepareForTracing prep(trc->runtime);
     MarkRuntime(trc);
 }
 
+static void
+IterateCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+                               JSIterateCompartmentCallback compartmentCallback,
+                               IterateArenaCallback arenaCallback,
+                               IterateCellCallback cellCallback)
+{
+    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
+        (*compartmentCallback)(rt, data, comp);
+
+    for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
+        JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind));
+        size_t thingSize = Arena::thingSize(AllocKind(thingKind));
+
+        for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) {
+            ArenaHeader *aheader = aiter.get();
+            (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize);
+            for (CellIterUnderGC iter(aheader); !iter.done(); iter.next())
+                (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize);
+        }
+    }
+}
+
 void
 js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
                                         IterateZoneCallback zoneCallback,
                                         JSIterateCompartmentCallback compartmentCallback,
                                         IterateArenaCallback arenaCallback,
                                         IterateCellCallback cellCallback)
 {
     AutoPrepareForTracing prop(rt);
 
     for (ZonesIter zone(rt); !zone.done(); zone.next()) {
         (*zoneCallback)(rt, data, zone);
-
-        for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
-            (*compartmentCallback)(rt, data, comp);
-
-        for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
-            JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind));
-            size_t thingSize = Arena::thingSize(AllocKind(thingKind));
+        IterateCompartmentsArenasCells(rt, zone, data,
+                                       compartmentCallback, arenaCallback, cellCallback);
+    }
+}
 
-            for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) {
-                ArenaHeader *aheader = aiter.get();
-                (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize);
-                for (CellIterUnderGC iter(aheader); !iter.done(); iter.next())
-                    (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize);
-            }
-        }
-    }
+void
+js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+                                       IterateZoneCallback zoneCallback,
+                                       JSIterateCompartmentCallback compartmentCallback,
+                                       IterateArenaCallback arenaCallback,
+                                       IterateCellCallback cellCallback)
+{
+    AutoPrepareForTracing prop(rt);
+
+    (*zoneCallback)(rt, data, zone);
+    IterateCompartmentsArenasCells(rt, zone, data,
+                                   compartmentCallback, arenaCallback, cellCallback);
 }
 
 void
 js::IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback)
 {
     AutoPrepareForTracing prep(rt);
 
     for (js::GCChunkSet::Range r = rt->gcChunkSet.all(); !r.empty(); r.popFront())
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1269,28 +1269,39 @@ MarkStackRangeConservatively(JSTracer *t
 typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk);
 typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone);
 typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena,
                                      JSGCTraceKind traceKind, size_t thingSize);
 typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing,
                                     JSGCTraceKind traceKind, size_t thingSize);
 
 /*
- * This function calls |compartmentCallback| on every compartment,
- * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
- * cell in the GC heap.
+ * This function calls |zoneCallback| on every zone, |compartmentCallback| on
+ * every compartment, |arenaCallback| on every in-use arena, and |cellCallback|
+ * on every in-use cell in the GC heap.
  */
 extern void
 IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
                                     IterateZoneCallback zoneCallback,
                                     JSIterateCompartmentCallback compartmentCallback,
                                     IterateArenaCallback arenaCallback,
                                     IterateCellCallback cellCallback);
 
 /*
+ * This function is like IterateZonesCompartmentsArenasCells, but does it for a
+ * single zone.
+ */
+extern void
+IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+                                   IterateZoneCallback zoneCallback,
+                                   JSIterateCompartmentCallback compartmentCallback,
+                                   IterateArenaCallback arenaCallback,
+                                   IterateCellCallback cellCallback);
+
+/*
  * Invoke chunkCallback on every in-use chunk.
  */
 extern void
 IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback);
 
 typedef void (*IterateScriptCallback)(JSRuntime *rt, void *data, JSScript *script);
 
 /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -38,16 +38,17 @@
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/OldDebugAPI.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
+#include "vm/ProxyObject.h"
 #include "vm/RegExpStaticsObject.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 
@@ -5675,18 +5676,34 @@ JSObject::addSizeOfExcludingThis(mozilla
             sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements);
 #endif
         } else {
             sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
         }
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
-    // Note that sizes->private_ is measured elsewhere.
-    if (is<ArgumentsObject>()) {
+    if (is<JSFunction>() ||
+        is<JSObject>() ||
+        is<ArrayObject>() ||
+        is<CallObject>() ||
+        is<RegExpObject>() ||
+        is<ProxyObject>())
+    {
+        // Do nothing.  But this function is hot, and we win by getting the
+        // common cases out of the way early.  Some stats on the most common
+        // classes, as measured during a vanilla browser session:
+        // - (53.7%, 53.7%): Function
+        // - (18.0%, 71.7%): Object
+        // - (16.9%, 88.6%): Array
+        // - ( 3.9%, 92.5%): Call
+        // - ( 2.8%, 95.3%): RegExp
+        // - ( 1.0%, 96.4%): Proxy
+
+    } else if (is<ArgumentsObject>()) {
         sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
     } else if (is<RegExpStaticsObject>()) {
         sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
     } else if (is<PropertyIteratorObject>()) {
         sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
 #ifdef JS_ION
     } else if (is<AsmJSModuleObject>()) {
         as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -373,17 +373,17 @@ class JSObject : public js::ObjectImpl
      * The number of allocated slots is not stored explicitly, and changes to
      * the slots must track changes in the slot span.
      */
     static bool growSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount,
                           uint32_t newCount);
     static void shrinkSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount,
                             uint32_t newCount);
 
-    bool hasDynamicSlots() const { return slots != nullptr; }
+    bool hasDynamicSlots() const { return !!slots; }
 
   protected:
     static inline bool updateSlotsForSpan(js::ThreadSafeContext *cx,
                                           js::HandleObject obj, size_t oldSpan, size_t newSpan);
 
   public:
     /*
      * Trigger the write barrier on a range of slots that will no longer be
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -18,18 +18,19 @@
 #include "jit/Ion.h"
 #include "vm/ArrayObject.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/WrapperObject.h"
 
 using mozilla::DebugOnly;
+using mozilla::MallocSizeOf;
+using mozilla::MoveRef;
 using mozilla::OldMove;
-using mozilla::MoveRef;
 using mozilla::PodEqual;
 
 using namespace js;
 
 using JS::RuntimeStats;
 using JS::ObjectPrivateVisitor;
 using JS::ZoneStats;
 using JS::CompartmentStats;
@@ -263,23 +264,20 @@ StatsCellCallback(JSRuntime *rt, void *d
             cStats->objectsGCHeapDenseArray += thingSize;
         else if (obj->is<CrossCompartmentWrapperObject>())
             cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
         else
             cStats->objectsGCHeapOrdinary += thingSize;
 
         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
 
-        // JSObject::sizeOfExcludingThis() doesn't measure objectsPrivate,
-        // so we do it here.
         if (ObjectPrivateVisitor *opv = closure->opv) {
             nsISupports *iface;
-            if (opv->getISupports_(obj, &iface) && iface) {
+            if (opv->getISupports_(obj, &iface) && iface)
                 cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
-            }
         }
         break;
       }
 
       case JSTRACE_STRING: {
         JSString *str = static_cast<JSString *>(thing);
 
         size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
@@ -333,17 +331,16 @@ StatsCellCallback(JSRuntime *rt, void *d
         cStats->shapesGCHeapBase += thingSize;
         break;
       }
 
       case JSTRACE_SCRIPT: {
         JSScript *script = static_cast<JSScript *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(script->compartment());
         cStats->scriptsGCHeap += thingSize;
-
         cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
         cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
 #ifdef JS_ION
         jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
                                    &cStats->baselineStubsFallback);
         cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
 #endif
 
@@ -441,24 +438,22 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
 
     IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
                   DecommittedArenasChunkCallback);
 
     // Take the per-compartment measurements.
     StatsClosure closure(rtStats, opv);
     if (!closure.init())
         return false;
-    rtStats->runtime.scriptSources = 0;
     IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
                                         StatsArenaCallback, StatsCellCallback);
 
     // Take the "explicit/js/runtime/" measurements.
     rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
 
-    rtStats->gcHeapGCThings = 0;
     for (size_t i = 0; i < rtStats->zoneStatsVector.length(); i++) {
         ZoneStats &zStats = rtStats->zoneStatsVector[i];
 
         rtStats->zTotals.add(zStats);
 
         // Move any strings which take up more than the sundries threshold
         // (counting all of their copies together) into notableStrings.
         FindNotableStrings(zStats);
@@ -526,8 +521,67 @@ JS::UserCompartmentCount(JSRuntime *rt)
 }
 
 JS_PUBLIC_API(size_t)
 JS::PeakSizeOfTemporary(const JSRuntime *rt)
 {
     return rt->tempLifoAlloc.peakSizeOfExcludingThis();
 }
 
+namespace JS {
+
+JS_PUBLIC_API(bool)
+AddSizeOfTab(JSRuntime *rt, JSObject *obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
+             TabSizes *sizes)
+{
+    class SimpleJSRuntimeStats : public JS::RuntimeStats
+    {
+      public:
+        SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
+          : JS::RuntimeStats(mallocSizeOf)
+        {}
+
+        virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats)
+            MOZ_OVERRIDE
+        {}
+
+        virtual void initExtraCompartmentStats(
+            JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE
+        {}
+    };
+
+    SimpleJSRuntimeStats rtStats(mallocSizeOf);
+
+    JS::Zone *zone = GetObjectZone(obj);
+
+    if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length()))
+        return false;
+
+    if (!rtStats.zoneStatsVector.reserve(1))
+        return false;
+
+    // Take the per-compartment measurements.
+    StatsClosure closure(&rtStats, opv);
+    if (!closure.init())
+        return false;
+    IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
+                                       StatsCompartmentCallback, StatsArenaCallback,
+                                       StatsCellCallback);
+
+    JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
+    rtStats.zTotals.add(rtStats.zoneStatsVector[0]);
+
+    for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
+        CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
+        rtStats.cTotals.add(cStats);
+    }
+
+    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
+        comp->compartmentStats = NULL;
+
+    rtStats.zTotals.addToTabSizes(sizes);
+    rtStats.cTotals.addToTabSizes(sizes);
+
+    return true;
+}
+
+} // namespace JS
+
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2411,17 +2411,17 @@ SizeOfTreeIncludingThis(nsINode *tree)
 class OrphanReporter : public JS::ObjectPrivateVisitor
 {
   public:
     OrphanReporter(GetISupportsFun aGetISupports)
       : JS::ObjectPrivateVisitor(aGetISupports)
     {
     }
 
-    virtual size_t sizeOfIncludingThis(nsISupports *aSupports) {
+    virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE {
         size_t n = 0;
         nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
         // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
         // that we have to skip XBL elements because they violate certain
         // assumptions.  Yuk.
         if (node && !node->IsInDoc() &&
             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
         {
@@ -2669,16 +2669,35 @@ JSReporter::CollectReports(WindowPaths *
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"),
                  KIND_HEAP, xpconnect,
                  "Memory used by XPConnect.");
 
     return NS_OK;
 }
 
+static nsresult
+JSSizeOfTab(JSObject *obj, size_t *jsObjectsSize, size_t *jsStringsSize,
+            size_t *jsPrivateSize, size_t *jsOtherSize)
+{
+    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+
+    TabSizes sizes;
+    OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
+    NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of,
+                                    &orphanReporter, &sizes),
+                   NS_ERROR_OUT_OF_MEMORY);
+
+    *jsObjectsSize = sizes.objects;
+    *jsStringsSize = sizes.strings;
+    *jsPrivateSize = sizes.private_;
+    *jsOtherSize   = sizes.other;
+    return NS_OK;
+}
+
 } // namespace xpc
 
 #ifdef MOZ_CRASHREPORTER
 static bool
 DiagnosticMemoryCallback(void *ptr, size_t size)
 {
     return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
 }
@@ -3035,16 +3054,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
 
     // Register memory reporters and distinguished amount functions.
     NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
     NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
     RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
+    mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
     if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(runtime);
 #endif
 }
 
--- a/layout/base/nsArenaMemoryStats.h
+++ b/layout/base/nsArenaMemoryStats.h
@@ -1,20 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsArenaMemoryStats_h
 #define nsArenaMemoryStats_h
 
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+
+class nsTabSizes {
+public:
+  enum Kind {
+      DOM,        // DOM stuff.
+      Style,      // Style stuff.
+      Other       // Everything else.
+  };
+
+  nsTabSizes() { mozilla::PodZero(this); }
+
+  void add(Kind kind, size_t n)
+  {
+    switch (kind) {
+      case DOM:   mDom   += n; break;
+      case Style: mStyle += n; break;
+      case Other: mOther += n; break;
+      default:    MOZ_CRASH("bad nsTabSizes kind");
+    }
+  }
+
+  size_t mDom;
+  size_t mStyle;
+  size_t mOther;
+};
+
 #define FRAME_ID_STAT_FIELD(classname) mArena##classname
 
 struct nsArenaMemoryStats {
-#define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname);
-#include "nsFrameIdList.h"
-#undef FRAME_ID
-  size_t mLineBoxes;
-  size_t mRuleNodes;
-  size_t mStyleContexts;
-  size_t mOther;
+#define FOR_EACH_SIZE(macro) \
+  macro(Other, mLineBoxes) \
+  macro(Style, mRuleNodes) \
+  macro(Style, mStyleContexts) \
+  macro(Other, mOther)
+
+  nsArenaMemoryStats()
+    :
+      #define ZERO_SIZE(kind, mSize) mSize(0),
+      FOR_EACH_SIZE(ZERO_SIZE)
+      #undef ZERO_SIZE
+      #define FRAME_ID(classname) FRAME_ID_STAT_FIELD(classname)(),
+      #include "nsFrameIdList.h"
+      #undef FRAME_ID
+      dummy()
+  {}
+
+  void addToTabSizes(nsTabSizes *sizes) const
+  {
+    #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
+    FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+    #undef ADD_TO_TAB_SIZES
+    #define FRAME_ID(classname) \
+      sizes->add(nsTabSizes::Other, FRAME_ID_STAT_FIELD(classname));
+    #include "nsFrameIdList.h"
+    #undef FRAME_ID
+  }
+
+  #define DECL_SIZE(kind, mSize) size_t mSize;
+  FOR_EACH_SIZE(DECL_SIZE)
+  #undef DECL_SIZE
+  #define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname);
+  #include "nsFrameIdList.h"
+  #undef FRAME_ID
+  int dummy;  // present just to absorb the trailing comma from FRAME_ID in the
+              // constructor
+
+#undef FOR_EACH_SIZE
 };
 
 #endif // nsArenaMemoryStats_h
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6086,18 +6086,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     }
   }
 
   if (aEvent->eventStructType == NS_KEY_EVENT &&
       mDocument && mDocument->EventHandlingSuppressed()) {
     if (aEvent->message == NS_KEY_DOWN) {
       mNoDelayedKeyEvents = true;
     } else if (!mNoDelayedKeyEvents) {
-      nsDelayedEvent* event =
-        new nsDelayedKeyEvent(aEvent->AsKeyboardEvent());
+      DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent());
       if (!mDelayedEvents.AppendElement(event)) {
         delete event;
       }
     }
     return NS_OK;
   }
 
   nsIFrame* frame = aFrame;
@@ -6308,17 +6307,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
     // Suppress mouse event if it's being targeted at an element inside
     // a document which needs events suppressed
     if (aEvent->eventStructType == NS_MOUSE_EVENT &&
         frame->PresContext()->Document()->EventHandlingSuppressed()) {
       if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
         mNoDelayedMouseEvents = true;
       } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
-        nsDelayedEvent* event = new nsDelayedMouseEvent(aEvent->AsMouseEvent());
+        DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
         if (!mDelayedEvents.AppendElement(event)) {
           delete event;
         }
       }
 
       return NS_OK;
     }
 
@@ -6841,18 +6840,18 @@ PresShell::HandleEventInternal(WidgetEve
         return NS_OK;
       }
       if (mouseEvent->IsShift()) {
         aEvent->mFlags.mOnlyChromeDispatch = true;
         aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
       }
     }
 
-    nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
-                                                          aEvent, mDocument);
+    AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
+                                                        aEvent, mDocument);
 
     if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) {
       nsIPresShell::AllowMouseCapture(
         nsEventStateManager::GetActiveEventStateManager() == manager);
     }
 
     nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
 
@@ -7601,19 +7600,19 @@ PresShell::FireOrClearDelayedEvents(bool
     mDelayedEvents.Clear();
     return;
   }
 
   if (mDocument) {
     nsCOMPtr<nsIDocument> doc = mDocument;
     while (!mIsDestroying && mDelayedEvents.Length() &&
            !doc->EventHandlingSuppressed()) {
-      nsAutoPtr<nsDelayedEvent> ev(mDelayedEvents[0].forget());
+      nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget());
       mDelayedEvents.RemoveElementAt(0);
-      ev->Dispatch(this);
+      ev->Dispatch();
     }
     if (!doc->EventHandlingSuppressed()) {
       mDelayedEvents.Clear();
     }
   }
 }
 
 static void
@@ -8314,16 +8313,66 @@ nsIPresShell::RemovePostRefreshObserver(
   presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
   return true;
 }
 
 //------------------------------------------------------
 // End of protected and private methods on the PresShell
 //------------------------------------------------------
 
+//------------------------------------------------------------------
+//-- Delayed event Classes Impls
+//------------------------------------------------------------------
+
+PresShell::DelayedInputEvent::DelayedInputEvent() :
+  DelayedEvent(),
+  mEvent(nullptr)
+{
+}
+
+PresShell::DelayedInputEvent::~DelayedInputEvent()
+{
+  delete mEvent;
+}
+
+void
+PresShell::DelayedInputEvent::Dispatch()
+{
+  if (!mEvent || !mEvent->widget) {
+    return;
+  }
+  nsCOMPtr<nsIWidget> widget = mEvent->widget;
+  nsEventStatus status;
+  widget->DispatchEvent(mEvent, status);
+}
+
+PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) :
+  DelayedInputEvent()
+{
+  WidgetMouseEvent* mouseEvent =
+    new WidgetMouseEvent(aEvent->mFlags.mIsTrusted,
+                         aEvent->message,
+                         aEvent->widget,
+                         aEvent->reason,
+                         aEvent->context);
+  mouseEvent->AssignMouseEventData(*aEvent, false);
+  mEvent = mouseEvent;
+}
+
+PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
+  DelayedInputEvent()
+{
+  WidgetKeyboardEvent* keyEvent =
+    new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted,
+                            aEvent->message,
+                            aEvent->widget);
+  keyEvent->AssignKeyEventData(*aEvent, false);
+  mEvent = keyEvent;
+}
+
 // Start of DEBUG only code
 
 #ifdef DEBUG
 
 static void
 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
 {
   nsAutoString n1, n2;
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -29,19 +29,18 @@
 #include "nsCRT.h"
 #include "nsAutoPtr.h"
 #include "nsIWidget.h"
 #include "nsStyleSet.h"
 #include "nsFrameSelection.h"
 #include "nsContentUtils.h" // For AddScriptBlocker().
 #include "nsRefreshDriver.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/MouseEvents.h"
-#include "mozilla/TextEvents.h"
 
 class nsRange;
 class nsIDragService;
 class nsCSSStyleSheet;
 
 struct RangePaintInfo;
 struct nsCallbackEventRequest;
 #ifdef MOZ_REFLOW_PERF
@@ -542,77 +541,45 @@ protected:
     nsresult rv = NS_OK;
     if (GetCurrentEventFrame()) {
       rv = HandleEventInternal(aEvent, aStatus);
     }
     PopCurrentEventInfo();
     return rv;
   }
 
-  class nsDelayedEvent
-  {
-  public:
-    virtual ~nsDelayedEvent() {};
-    virtual void Dispatch(PresShell* aShell) {}
-  };
-
-  class nsDelayedInputEvent : public nsDelayedEvent
+  class DelayedEvent
   {
   public:
-    virtual void Dispatch(PresShell* aShell)
-    {
-      if (mEvent && mEvent->widget) {
-        nsCOMPtr<nsIWidget> w = mEvent->widget;
-        nsEventStatus status;
-        w->DispatchEvent(mEvent, status);
-      }
-    }
+    virtual ~DelayedEvent() { }
+    virtual void Dispatch() { }
+  };
+
+  class DelayedInputEvent : public DelayedEvent
+  {
+  public:
+    virtual void Dispatch() MOZ_OVERRIDE;
 
   protected:
-    nsDelayedInputEvent()
-    : nsDelayedEvent(), mEvent(nullptr) {}
-
-    virtual ~nsDelayedInputEvent()
-    {
-      delete mEvent;
-    }
+    DelayedInputEvent();
+    virtual ~DelayedInputEvent();
 
     mozilla::WidgetInputEvent* mEvent;
   };
 
-  class nsDelayedMouseEvent : public nsDelayedInputEvent
+  class DelayedMouseEvent : public DelayedInputEvent
   {
   public:
-    nsDelayedMouseEvent(mozilla::WidgetMouseEvent* aEvent) :
-      nsDelayedInputEvent()
-    {
-      mozilla::WidgetMouseEvent* mouseEvent =
-        new mozilla::WidgetMouseEvent(aEvent->mFlags.mIsTrusted,
-                                      aEvent->message,
-                                      aEvent->widget,
-                                      aEvent->reason,
-                                      aEvent->context);
-      mouseEvent->AssignMouseEventData(*aEvent, false);
-      mEvent = mouseEvent;
-    }
+    DelayedMouseEvent(mozilla::WidgetMouseEvent* aEvent);
   };
 
-  class nsDelayedKeyEvent : public nsDelayedInputEvent
+  class DelayedKeyEvent : public DelayedInputEvent
   {
   public:
-    nsDelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) :
-      nsDelayedInputEvent()
-    {
-      mozilla::WidgetKeyboardEvent* keyEvent =
-        new mozilla::WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted,
-                                         aEvent->message,
-                                         aEvent->widget);
-      keyEvent->AssignKeyEventData(*aEvent, false);
-      mEvent = keyEvent;
-    }
+    DelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent);
   };
 
   // Check if aEvent is a mouse event and record the mouse location for later
   // synth mouse moves.
   void RecordMouseLocation(mozilla::WidgetGUIEvent* aEvent);
   class nsSynthMouseMoveEvent MOZ_FINAL : public nsARefreshObserver {
   public:
     nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll)
@@ -754,17 +721,17 @@ protected:
 
   // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
   // we finish reflowing mCurrentReflowRoot.
   nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesToDirty;
 
   // Reflow roots that need to be reflowed.
   nsTArray<nsIFrame*>       mDirtyRoots;
 
-  nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
+  nsTArray<nsAutoPtr<DelayedEvent> > mDelayedEvents;
   nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
   nsCOMPtr<nsITimer>        mAsyncResizeEventTimer;
 private:
   nsIFrame*                 mCurrentEventFrame;
   nsCOMPtr<nsIContent>      mCurrentEventContent;
   nsTArray<nsIFrame*>       mCurrentEventFrameStack;
   nsCOMArray<nsIContent>    mCurrentEventContentStack;
 protected:
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -9,17 +9,17 @@
 
 #include "nsIWeakReference.h"
 
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsRange.h"
 #include "nsThreadUtils.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 
 struct CachedOffsetForFrame;
 class nsAutoScrollTimer;
 class nsIContentIterator;
 class nsIFrame;
 class nsFrameSelection;
 struct SelectionDetails;
 
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsFrameSelection_h___
 #define nsFrameSelection_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/Selection.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsISelectionController.h"
 #include "nsITableCellLayout.h"
 #include "nsIDOMElement.h"
 #include "nsRange.h"
 
 class nsTableOuterFrame;
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -140,17 +140,16 @@ extern "C" {
   extern void DisposeGWorld(GWorldPtr offscreenGWorld)
     __attribute__((weak_import));
   #endif /* __QDOFFSCREEN__ */
 }
 
 #endif /* #if defined(XP_MACOSX) && !defined(__LP64__) */
 
 using namespace mozilla;
-using namespace mozilla::plugins;
 using namespace mozilla::layers;
 
 class PluginBackgroundSink : public ReadbackSink {
 public:
   PluginBackgroundSink(nsObjectFrame* aFrame, uint64_t aStartSequenceNumber)
     : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {}
   ~PluginBackgroundSink()
   {
@@ -1786,17 +1785,17 @@ nsObjectFrame::PaintPlugin(nsDisplayList
       nsPoint origin;
 
       gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect);
       if (nativeDraw.IsDoublePass()) {
         // OOP plugin specific: let the shim know before we paint if we are doing a
         // double pass render. If this plugin isn't oop, the register window message
         // will be ignored.
         NPEvent pluginEvent;
-        pluginEvent.event = DoublePassRenderingEvent();
+        pluginEvent.event = plugins::DoublePassRenderingEvent();
         pluginEvent.wParam = 0;
         pluginEvent.lParam = 0;
         if (pluginEvent.event)
           inst->HandleEvent(&pluginEvent, nullptr);
       }
       do {
         HDC hdc = nativeDraw.BeginNativeDrawing();
         if (!hdc)
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2415,18 +2415,19 @@ private:
   nsStyleCoord mFilterParameter; // coord, percent, factor, angle
   union {
     nsIURI* mURL;
     nsCSSShadowArray* mDropShadow;
   };
 };
 
 template<>
-struct nsTArray_CopyElements<nsStyleFilter>
-  : public nsTArray_CopyWithConstructors<nsStyleFilter> {};
+struct nsTArray_CopyChooser<nsStyleFilter> {
+  typedef nsTArray_CopyWithConstructors<nsStyleFilter> Type;
+};
 
 struct nsStyleSVGReset {
   nsStyleSVGReset();
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -14,16 +14,17 @@
 #include "nsINameSpaceManager.h"
 #include "nsViewManager.h"
 #include "nsWidgetsCID.h"
 #include "nsMenuFrame.h"
 #include "nsMenuBarFrame.h"
 #include "nsPopupSetFrame.h"
 #include "nsEventDispatcher.h"
 #include "nsPIDOMWindow.h"
+#include "nsIDOMKeyEvent.h"
 #include "nsIDOMScreen.h"
 #include "nsIPresShell.h"
 #include "nsFrameManager.h"
 #include "nsIDocument.h"
 #include "nsRect.h"
 #include "nsIComponentManager.h"
 #include "nsBoxLayoutState.h"
 #include "nsIScrollableFrame.h"
@@ -1645,17 +1646,17 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
   bool isMenu = parentContent &&
                   !parentContent->NodeInfo()->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL);
 
   static DOMTimeStamp lastKeyTime = 0;
   DOMTimeStamp keyTime;
   aKeyEvent->GetTimeStamp(&keyTime);
 
   if (charCode == 0) {
-    if (keyCode == NS_VK_BACK) {
+    if (keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
       if (!isMenu && !mIncrementalString.IsEmpty()) {
         mIncrementalString.SetLength(mIncrementalString.Length() - 1);
         return nullptr;
       }
       else {
 #ifdef XP_WIN
         nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
         if (soundInterface)
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -1841,17 +1841,18 @@ nsXULPopupManager::HandleKeyboardNavigat
   if (item)
     itemFrame = item->Frame();
   else if (mActiveMenuBar)
     itemFrame = mActiveMenuBar;
   else
     return false;
 
   nsNavigationDirection theDirection;
-  NS_ASSERTION(aKeyCode >= NS_VK_END && aKeyCode <= NS_VK_DOWN, "Illegal key code");
+  NS_ASSERTION(aKeyCode >= nsIDOMKeyEvent::DOM_VK_END &&
+                 aKeyCode <= nsIDOMKeyEvent::DOM_VK_DOWN, "Illegal key code");
   theDirection = NS_DIRECTION_FROM_KEY_CODE(itemFrame, aKeyCode);
 
   // if a popup is open, first check for navigation within the popup
   if (item && HandleKeyboardNavigationInPopup(item, theDirection))
     return true;
 
   // no popup handled the key, so check the active menubar, if any
   if (mActiveMenuBar) {
@@ -2349,18 +2350,18 @@ nsXULMenuCommandEvent::Run()
     nsPresContext* presContext = menuFrame->PresContext();
     nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
     nsRefPtr<nsViewManager> kungFuDeathGrip = shell->GetViewManager();
 
     // Deselect ourselves.
     if (mCloseMenuMode != CloseMenuMode_None)
       menuFrame->SelectMenu(false);
 
-    nsAutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr,
-                                                          shell->GetDocument());
+    AutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr,
+                                                        shell->GetDocument());
     nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nullptr, shell,
                                        mControl, mAlt, mShift, mMeta);
   }
 
   if (popup && mCloseMenuMode != CloseMenuMode_None)
     pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false);
 
   return NS_OK;
--- a/mobile/android/chrome/content/InputWidgetHelper.js
+++ b/mobile/android/chrome/content/InputWidgetHelper.js
@@ -8,17 +8,17 @@ var InputWidgetHelper = {
 
   handleEvent: function(aEvent) {
     this.handleClick(aEvent.target);
   },
 
   handleClick: function(aTarget) {
     // if we're busy looking at a InputWidget we want to eat any clicks that
     // come to us, but not to process them
-    if (this._uiBusy || !this._isValidInput(aTarget))
+    if (this._uiBusy || !this.hasInputWidget(aTarget))
       return;
 
     this._uiBusy = true;
     this.show(aTarget);
     this._uiBusy = false;
   },
 
   show: function(aElement) {
@@ -55,17 +55,17 @@ var InputWidgetHelper = {
       }
       // Else the user canceled the input.
 
       if (changed)
         this.fireOnChange(aElement);
     }).bind(this));
   },
 
-  _isValidInput: function(aElement) {
+  hasInputWidget: function(aElement) {
     if (!aElement instanceof HTMLInputElement)
       return false;
 
     let type = aElement.getAttribute('type');
     if (type == "date" || type == "datetime" || type == "datetime-local" ||
         type == "week" || type == "month" || type == "time") {
       return true;
     }
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -281,17 +281,17 @@ var SelectionHandler = {
    * Initializes SelectionHandler and positions the caret handle.
    *
    * @param aX, aY tap location in client coordinates.
    */
   attachCaret: function sh_attachCaret(aElement) {
     this._initTargetInfo(aElement);
 
     this._contentWindow.addEventListener("keydown", this, false);
-    this._contentWindow.addEventListener("blur", this, false);
+    this._contentWindow.addEventListener("blur", this, true);
 
     this._activeType = this.TYPE_CURSOR;
     this._positionHandles();
 
     sendMessageToJava({
       type: "TextSelection:ShowHandles",
       handles: [this.HANDLE_TYPE_MIDDLE]
     });
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4303,18 +4303,19 @@ var BrowserEventHandler = {
               [x, y] = this._moveClickPoint(element, x, y);
               element = ElementTouchHelper.anyElementFromPoint(x, y);
             }
 
             this._sendMouseEvent("mousemove", element, x, y);
             this._sendMouseEvent("mousedown", element, x, y);
             this._sendMouseEvent("mouseup",   element, x, y);
 
-            // See if its an input element, and it isn't disabled
+            // See if its an input element, and it isn't disabled, nor handled by Android native dialog
             if (!element.disabled &&
+                !InputWidgetHelper.hasInputWidget(element) &&
                 ((element instanceof HTMLInputElement && element.mozIsTextField(false)) ||
                 (element instanceof HTMLTextAreaElement)))
               SelectionHandler.attachCaret(element);
 
             // scrollToFocusedInput does its own checks to find out if an element should be zoomed into
             BrowserApp.scrollToFocusedInput(BrowserApp.selectedBrowser);
           } catch(e) {
             Cu.reportError(e);
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -149,17 +149,17 @@ int main(int argc, char **argv) {
       argc -= 2;
     } 
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
     /* -D DERFilePath, also matches -D[index] DERFilePath
        We allow an index for verifying to be symmetric
        with the import and export command line arguments. */
     else if (argv[1][0] == '-' &&
              argv[1][1] == 'D' &&
-             (argv[1][2] == '0' + certCount || argv[1][2] == '\0')) {
+             (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) {
       if (certCount >= MAX_SIGNATURES) {
         print_usage();
         return -1;
       }
       DERFilePaths[certCount++] = argv[2];
       argv += 2;
       argc -= 2;
     }
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -610,20 +610,19 @@ def main():
             log.info("Running tests %d-%d/%d", start+1, end, len(tests))
 
         deviceRoot = dm.getDeviceRoot()      
         dm.removeFile(os.path.join(deviceRoot, "fennec_ids.txt"))
         fennec_ids = os.path.abspath("fennec_ids.txt")
         if not os.path.exists(fennec_ids) and options.robocopIds:
             fennec_ids = options.robocopIds
         dm.pushFile(fennec_ids, os.path.join(deviceRoot, "fennec_ids.txt"))
-        options.extraPrefs.append('robocop.logfile="%s/robocop.log"' % deviceRoot)
         options.extraPrefs.append('browser.search.suggest.enabled=true')
         options.extraPrefs.append('browser.search.suggest.prompted=true')
-        options.extraPrefs.append('layout.css.devPixelsPerPx="1.0"')
+        options.extraPrefs.append('layout.css.devPixelsPerPx=1.0')
         options.extraPrefs.append('browser.chrome.dynamictoolbar=false')
 
         if (options.dm_trans == 'adb' and options.robocopApk):
             dm._checkCmd(["install", "-r", options.robocopApk])
 
         retVal = None
         for test in robocop_tests:
             if options.testPath and options.testPath != test['name']:
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -150,16 +150,31 @@
       // and this file hasn't been updated appropriately.
       dummy = mgr[amounts[i]];
       ok(dummy !== undefined,
          "accessed an unknown distinguished amount: " + amounts[i]);
     } catch (ex) {
     }
   }
 
+  // Run sizeOfTab() to make sure it doesn't crash.  We can't check the result
+  // values because they're non-deterministic.
+  let jsObjectsSize = {};
+  let jsStringsSize = {};
+  let jsOtherSize = {};
+  let domSize = {};
+  let styleSize = {};
+  let otherSize = {};
+  let totalSize = {};
+  let jsMilliseconds = {};
+  let nonJSMilliseconds = {};
+  mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
+                domSize, styleSize, otherSize, totalSize,
+                jsMilliseconds, nonJSMilliseconds);
+
   let e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     r.collectReports(handleReport, null);
 
     // Access |name| to make sure it doesn't crash or assert.
     dummy = r.name;
   }
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -235,25 +235,19 @@
                         <tr>
                             <td class="statLabel">&stats.volume;</td> <td class="statValue"><span class="statVolume"/></td>
                         </tr>
                         <tr>
                             <!-- Localization note: readyState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
                             <td class="statLabel">readyState</td> <td class="statValue"><span class="statReadyState"/></td>
                         </tr>
                         <tr>
-                            <td class="statLabel">&stats.channels;</td> <td class="statValue"><span class="statChannels"/></td>
-                        </tr>
-                        <tr>
                           <!-- Localization note: networkState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
                           <td class="statLabel">networkState</td> <td class="statValue"><span class="statNetState"/></td>
                         </tr>
-                        <tr>
-                          <td class="statLabel">&stats.sampleRate;</td> <td class="statValue"><span class="statSampleRate"/></td>
-                        </tr>
                         <tr style="height: 1em;"/>
 
                         <tr>
                             <td class="statLabel">&stats.framesParsed;</td>
                             <td class="statValue"><span class="statFramesParsed"/></td>
                         </tr>
                         <tr>
                             <td class="statLabel">&stats.framesDecoded;</td>
@@ -1213,19 +1207,16 @@
                     s.framesDecoded.textContent = v.mozDecodedFrames;
                     s.framesPresented.textContent = v.mozPresentedFrames;
                     s.framesPainted.textContent = v.mozPaintedFrames;
 
                     let volume = Math.round(v.volume * 100) + "%";
                     if (v.muted)
                         volume += " (muted)";
                     s.volume.textContent = volume;
-
-                    s.channels.textContent = v.mozChannels;
-                    s.sampRate.textContent = (v.mozSampleRate / 1000).toFixed(3) + " kHz";
                 },
 
                 keyHandler : function(event) {
                     // Ignore keys when content might be providing its own.
                     if (!this.video.hasAttribute("controls"))
                         return;
 
                     var keystroke = "";
@@ -1405,18 +1396,16 @@
                     this.clickToPlay        = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
                     this.fullscreenButton   = document.getAnonymousElementByAttribute(binding, "class", "fullscreenButton");
 
                     this.statsTable       = document.getAnonymousElementByAttribute(binding, "class", "statsTable");
                     this.stats.filename   = document.getAnonymousElementByAttribute(binding, "class", "statFilename");
                     this.stats.size       = document.getAnonymousElementByAttribute(binding, "class", "statSize");
                     this.stats.activity   = document.getAnonymousElementByAttribute(binding, "class", "statActivity");
                     this.stats.volume     = document.getAnonymousElementByAttribute(binding, "class", "statVolume");
-                    this.stats.channels   = document.getAnonymousElementByAttribute(binding, "class", "statChannels");
-                    this.stats.sampRate   = document.getAnonymousElementByAttribute(binding, "class", "statSampleRate");
                     this.stats.readyState = document.getAnonymousElementByAttribute(binding, "class", "statReadyState");
                     this.stats.netState   = document.getAnonymousElementByAttribute(binding, "class", "statNetState");
                     this.stats.framesParsed    = document.getAnonymousElementByAttribute(binding, "class", "statFramesParsed");
                     this.stats.framesDecoded   = document.getAnonymousElementByAttribute(binding, "class", "statFramesDecoded");
                     this.stats.framesPresented = document.getAnonymousElementByAttribute(binding, "class", "statFramesPresented");
                     this.stats.framesPainted   = document.getAnonymousElementByAttribute(binding, "class", "statFramesPainted");
 
                     this.setupInitialState();
--- a/toolkit/locales/en-US/chrome/global/videocontrols.dtd
+++ b/toolkit/locales/en-US/chrome/global/videocontrols.dtd
@@ -12,18 +12,16 @@
 <!ENTITY stats.media "Media">
 <!ENTITY stats.size "Size">
 <!ENTITY stats.activity "Activity">
 <!ENTITY stats.activityPaused "Paused">
 <!ENTITY stats.activityPlaying "Playing">
 <!ENTITY stats.activityEnded "Ended">
 <!ENTITY stats.activitySeeking "(seeking)">
 <!ENTITY stats.volume "Volume">
-<!ENTITY stats.channels "Channels">
-<!ENTITY stats.sampleRate "Sample Rate">
 <!ENTITY stats.framesParsed "Frames parsed">
 <!ENTITY stats.framesDecoded "Frames decoded">
 <!ENTITY stats.framesPresented "Frames presented">
 <!ENTITY stats.framesPainted "Frames painted">
 
 <!ENTITY error.aborted "Video loading stopped.">
 <!ENTITY error.network "Video playback aborted due to a network error.">
 <!ENTITY error.decode "Video can't be played because the file is corrupt.">
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -615,22 +615,24 @@ SyncProfile* NewSyncProfile()
 
 SyncProfile* TableTicker::GetBacktrace()
 {
   SyncProfile* profile = NewSyncProfile();
 
   TickSample sample;
   sample.threadProfile = profile;
 
+#if defined(HAVE_NATIVE_UNWIND)
 #if defined(XP_WIN) || defined(LINUX)
   tickcontext_t context;
   sample.PopulateContext(&context);
 #elif defined(XP_MACOSX)
   sample.PopulateContext(nullptr);
 #endif
+#endif
 
   sample.isSamplingCurrentThread = true;
   sample.timestamp = mozilla::TimeStamp::Now();
 
   if (!HasUnwinderThread()) {
     profile->BeginUnwind();
   }
 
--- a/view/public/nsView.h
+++ b/view/public/nsView.h
@@ -6,16 +6,17 @@
 #ifndef nsView_h__
 #define nsView_h__
 
 #include "nsCoord.h"
 #include "nsRect.h"
 #include "nsPoint.h"
 #include "nsRegion.h"
 #include "nsCRT.h"
+#include "nsWidgetInitData.h" // for nsWindowType
 #include "nsIWidgetListener.h"
 #include "mozilla/EventForwards.h"
 
 class nsViewManager;
 class nsIWidget;
 class nsIFrame;
 
 // Enumerated type to indicate the visibility of a layer.
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/StartupTimeline.h"
 #include "GeckoProfiler.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h" // for nsAutoScriptBlocker
 #include "nsLayoutUtils.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
+#include "nsIDocument.h"
 
 /**
    XXX TODO XXX
 
    DeCOMify newly private methods
    Optimize view storage
 */
 
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -59,16 +59,18 @@ namespace mozilla {
 #undef NS_EVENT_CLASS
 #undef NS_ROOT_EVENT_CLASS
 
 // BasicEvents.h
 struct EventFlags;
 
 // TextEvents.h
 struct AlternativeCharCode;
+
+// TextRange.h
 struct TextRangeStyle;
 struct TextRange;
 
 typedef TextRange* TextRangeArray;
 
 } // namespace mozilla
 
 #endif // mozilla_EventForwards_h__
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -6,23 +6,22 @@
 #ifndef mozilla_TextEvents_h__
 #define mozilla_TextEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily
-#include "nsColor.h"
+#include "mozilla/TextRange.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsITransferable.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
-#include "nsStyleConsts.h"
 #include "nsTArray.h"
 
 /******************************************************************************
  * virtual keycode values
  ******************************************************************************/
 
 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) NS_##aDOMKeyName = aDOMKeyCode
 
@@ -145,158 +144,16 @@ public:
     // Don't copy mNativeKeyEvent because it may be referred after its instance
     // is destroyed.
     mNativeKeyEvent = nullptr;
     mUniqueId = aEvent.mUniqueId;
   }
 };
 
 /******************************************************************************
- * mozilla::TextRangeStyle
- ******************************************************************************/
-
-struct TextRangeStyle
-{
-  enum {
-    LINESTYLE_NONE   = NS_STYLE_TEXT_DECORATION_STYLE_NONE,
-    LINESTYLE_SOLID  = NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
-    LINESTYLE_DOTTED = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED,
-    LINESTYLE_DASHED = NS_STYLE_TEXT_DECORATION_STYLE_DASHED,
-    LINESTYLE_DOUBLE = NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE,
-    LINESTYLE_WAVY   = NS_STYLE_TEXT_DECORATION_STYLE_WAVY
-  };
-
-  enum {
-    DEFINED_NONE             = 0x00,
-    DEFINED_LINESTYLE        = 0x01,
-    DEFINED_FOREGROUND_COLOR = 0x02,
-    DEFINED_BACKGROUND_COLOR = 0x04,
-    DEFINED_UNDERLINE_COLOR  = 0x08
-  };
-
-  // Initialize all members, because TextRange instances may be compared by
-  // memcomp.
-  TextRangeStyle()
-  {
-    Clear();
-  }
-
-  void Clear()
-  {
-    mDefinedStyles = DEFINED_NONE;
-    mLineStyle = LINESTYLE_NONE;
-    mIsBoldLine = false;
-    mForegroundColor = mBackgroundColor = mUnderlineColor = NS_RGBA(0, 0, 0, 0);
-  }
-
-  bool IsDefined() const { return mDefinedStyles != DEFINED_NONE; }
-
-  bool IsLineStyleDefined() const
-  {
-    return (mDefinedStyles & DEFINED_LINESTYLE) != 0;
-  }
-
-  bool IsForegroundColorDefined() const
-  {
-    return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0;
-  }
-
-  bool IsBackgroundColorDefined() const
-  {
-    return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0;
-  }
-
-  bool IsUnderlineColorDefined() const
-  {
-    return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0;
-  }
-
-  bool IsNoChangeStyle() const
-  {
-    return !IsForegroundColorDefined() && !IsBackgroundColorDefined() &&
-           IsLineStyleDefined() && mLineStyle == LINESTYLE_NONE;
-  }
-
-  bool Equals(const TextRangeStyle& aOther)
-  {
-    if (mDefinedStyles != aOther.mDefinedStyles)
-      return false;
-    if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle ||
-                                 !mIsBoldLine != !aOther.mIsBoldLine))
-      return false;
-    if (IsForegroundColorDefined() &&
-        (mForegroundColor != aOther.mForegroundColor))
-      return false;
-    if (IsBackgroundColorDefined() &&
-        (mBackgroundColor != aOther.mBackgroundColor))
-      return false;
-    if (IsUnderlineColorDefined() &&
-        (mUnderlineColor != aOther.mUnderlineColor))
-      return false;
-    return true;
-  }
-
-  bool operator !=(const TextRangeStyle &aOther)
-  {
-    return !Equals(aOther);
-  }
-
-  bool operator ==(const TextRangeStyle &aOther)
-  {
-    return Equals(aOther);
-  }
-
-  uint8_t mDefinedStyles;
-  uint8_t mLineStyle;        // DEFINED_LINESTYLE
-
-  bool mIsBoldLine;  // DEFINED_LINESTYLE
-
-  nscolor mForegroundColor;  // DEFINED_FOREGROUND_COLOR
-  nscolor mBackgroundColor;  // DEFINED_BACKGROUND_COLOR
-  nscolor mUnderlineColor;   // DEFINED_UNDERLINE_COLOR
-};
-
-/******************************************************************************
- * mozilla::TextRange
- ******************************************************************************/
-
-// Sync with nsIPrivateTextRange.h when you change these constants.
-#define NS_TEXTRANGE_CARETPOSITION         0x01
-#define NS_TEXTRANGE_RAWINPUT              0x02
-#define NS_TEXTRANGE_SELECTEDRAWTEXT       0x03
-#define NS_TEXTRANGE_CONVERTEDTEXT         0x04
-#define NS_TEXTRANGE_SELECTEDCONVERTEDTEXT 0x05
-
-struct TextRange
-{
-  TextRange() :
-    mStartOffset(0), mEndOffset(0), mRangeType(0)
-  {
-  }
-
-  uint32_t mStartOffset;
-  // XXX Storing end offset makes the initializing code very complicated.
-  //     We should replace it with mLength.
-  uint32_t mEndOffset;
-  uint32_t mRangeType;
-
-  TextRangeStyle mRangeStyle;
-
-  uint32_t Length() const { return mEndOffset - mStartOffset; }
-};
-
-/******************************************************************************
- * mozilla::TextRangeArray
- *
- * XXX This should be replaced with nsTArray<TextRange>.
- ******************************************************************************/
-
-typedef TextRange* TextRangeArray;
-
-/******************************************************************************
  * mozilla::WidgetTextEvent
  *
  * XXX WidgetTextEvent is fired with compositionupdate event almost every time.
  *     This wastes performance and the cost of mantaining each platform's
  *     implementation.  Therefore, we should merge WidgetTextEvent and
  *     WidgetCompositionEvent.  Then, DOM compositionupdate should be fired
  *     from TextComposition automatically.
  ******************************************************************************/
new file mode 100644
--- /dev/null
+++ b/widget/TextRange.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TextRage_h_
+#define mozilla_TextRage_h_
+
+#include <stdint.h>
+
+#include "nsColor.h"
+#include "nsStyleConsts.h"
+
+namespace mozilla {
+
+/******************************************************************************
+ * mozilla::TextRangeStyle
+ ******************************************************************************/
+
+struct TextRangeStyle
+{
+  enum
+  {
+    LINESTYLE_NONE   = NS_STYLE_TEXT_DECORATION_STYLE_NONE,
+    LINESTYLE_SOLID  = NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
+    LINESTYLE_DOTTED = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED,
+    LINESTYLE_DASHED = NS_STYLE_TEXT_DECORATION_STYLE_DASHED,
+    LINESTYLE_DOUBLE = NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE,
+    LINESTYLE_WAVY   = NS_STYLE_TEXT_DECORATION_STYLE_WAVY
+  };
+
+  enum
+  {
+    DEFINED_NONE             = 0x00,
+    DEFINED_LINESTYLE        = 0x01,
+    DEFINED_FOREGROUND_COLOR = 0x02,
+    DEFINED_BACKGROUND_COLOR = 0x04,
+    DEFINED_UNDERLINE_COLOR  = 0x08
+  };
+
+  // Initialize all members, because TextRange instances may be compared by
+  // memcomp.
+  TextRangeStyle()
+  {
+    Clear();
+  }
+
+  void Clear()
+  {
+    mDefinedStyles = DEFINED_NONE;
+    mLineStyle = LINESTYLE_NONE;
+    mIsBoldLine = false;
+    mForegroundColor = mBackgroundColor = mUnderlineColor = NS_RGBA(0, 0, 0, 0);
+  }
+
+  bool IsDefined() const { return mDefinedStyles != DEFINED_NONE; }
+
+  bool IsLineStyleDefined() const
+  {
+    return (mDefinedStyles & DEFINED_LINESTYLE) != 0;
+  }
+
+  bool IsForegroundColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0;
+  }
+
+  bool IsBackgroundColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0;
+  }
+
+  bool IsUnderlineColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0;
+  }
+
+  bool IsNoChangeStyle() const
+  {
+    return !IsForegroundColorDefined() && !IsBackgroundColorDefined() &&
+           IsLineStyleDefined() && mLineStyle == LINESTYLE_NONE;
+  }
+
+  bool Equals(const TextRangeStyle& aOther)
+  {
+    if (mDefinedStyles != aOther.mDefinedStyles)
+      return false;
+    if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle ||
+                                 !mIsBoldLine != !aOther.mIsBoldLine))
+      return false;
+    if (IsForegroundColorDefined() &&
+        (mForegroundColor != aOther.mForegroundColor))
+      return false;
+    if (IsBackgroundColorDefined() &&
+        (mBackgroundColor != aOther.mBackgroundColor))
+      return false;
+    if (IsUnderlineColorDefined() &&
+        (mUnderlineColor != aOther.mUnderlineColor))
+      return false;
+    return true;
+  }
+
+  bool operator !=(const TextRangeStyle &aOther)
+  {
+    return !Equals(aOther);
+  }
+
+  bool operator ==(const TextRangeStyle &aOther)
+  {
+    return Equals(aOther);
+  }
+
+  uint8_t mDefinedStyles;
+  uint8_t mLineStyle;        // DEFINED_LINESTYLE
+
+  bool mIsBoldLine;  // DEFINED_LINESTYLE
+
+  nscolor mForegroundColor;  // DEFINED_FOREGROUND_COLOR
+  nscolor mBackgroundColor;  // DEFINED_BACKGROUND_COLOR
+  nscolor mUnderlineColor;   // DEFINED_UNDERLINE_COLOR
+};
+
+/******************************************************************************
+ * mozilla::TextRange
+ ******************************************************************************/
+
+// Sync with nsIPrivateTextRange.h when you change these constants.
+#define NS_TEXTRANGE_CARETPOSITION         0x01
+#define NS_TEXTRANGE_RAWINPUT              0x02
+#define NS_TEXTRANGE_SELECTEDRAWTEXT       0x03
+#define NS_TEXTRANGE_CONVERTEDTEXT         0x04
+#define NS_TEXTRANGE_SELECTEDCONVERTEDTEXT 0x05
+
+struct TextRange
+{
+  TextRange() :
+    mStartOffset(0), mEndOffset(0), mRangeType(0)
+  {
+  }
+
+  uint32_t mStartOffset;
+  // XXX Storing end offset makes the initializing code very complicated.
+  //     We should replace it with mLength.
+  uint32_t mEndOffset;
+  uint32_t mRangeType;
+
+  TextRangeStyle mRangeStyle;
+
+  uint32_t Length() const { return mEndOffset - mStartOffset; }
+};
+
+/******************************************************************************
+ * mozilla::TextRangeArray
+ *
+ * XXX This should be replaced with nsTArray<TextRange>.
+ ******************************************************************************/
+
+typedef TextRange* TextRangeArray;
+
+} // namespace mozilla
+
+#endif // mozilla_TextRage_h_
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -6,18 +6,19 @@
 #ifndef NSWINDOW_H_
 #define NSWINDOW_H_
 
 #include "nsBaseWidget.h"
 #include "gfxPoint.h"
 #include "nsIIdleServiceInternal.h"
 #include "nsTArray.h"
 #include "AndroidJavaWrappers.h"
+#include "mozilla/EventForwards.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 
 class gfxASurface;
 
 struct ANPEvent;
 
 namespace mozilla {
     class AndroidGeckoEvent;
 
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -17,16 +17,17 @@
 #include "prlog.h"
 #include "nsTArray.h"
 #include "nsPrimitiveHelpers.h"
 #include "prtime.h"
 #include "prthread.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include "nsCRT.h"
+#include "mozilla/BasicEvents.h"
 #include "mozilla/Services.h"
 
 #include "gfxASurface.h"
 #include "gfxXlibSurface.h"
 #include "gfxContext.h"
 #include "nsImageToPixbuf.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -25,17 +25,17 @@
 
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #endif /* MOZ_X11 */
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
 #endif
-#include "mozilla/MouseEvents.h"
+#include "mozilla/EventForwards.h"
 
 #include "nsGtkIMModule.h"
 
 #undef LOG
 #ifdef MOZ_LOGGING
 
 // make sure that logging is enabled before including prlog.h
 #define FORCE_PR_LOG
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -113,16 +113,17 @@ EXPORTS.mozilla += [
     'BasicEvents.h',
     'ContentEvents.h',
     'EventClassList.h',
     'EventForwards.h',
     'LookAndFeel.h',
     'MiscEvents.h',
     'MouseEvents.h',
     'TextEvents.h',
+    'TextRange.h',
     'TouchEvents.h',
     'WidgetUtils.h',
 ]
 
 if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
     EXPORTS.mozilla += [
         'WidgetTraceEvent.h',
     ]
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -1,22 +1,24 @@
 /* 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 nsIWidgetListener_h__
 #define nsIWidgetListener_h__
 
-#include "nscore.h"
-#include "nsIXULWindow.h"
-#include "nsRegion.h"
-#include "mozilla/BasicEvents.h"
+#include <stdint.h>
+
+#include "mozilla/EventForwards.h"
 
 class nsView;
+class nsIntRegion;
 class nsIPresShell;
+class nsIWidget;
+class nsIXULWindow;
 
 /**
  * sizemode is an adjunct to widget size
  */
 enum nsSizeMode
 {
   nsSizeMode_Normal = 0,
   nsSizeMode_Minimized,
@@ -38,107 +40,106 @@ class nsIWidgetListener
 {
 public:
 
   /**
    * If this listener is for an nsIXULWindow, return it. If this is null, then
    * this is likely a listener for a view, which can be determined using
    * GetView. If both methods return null, this will be an nsWebBrowser.
    */
-  virtual nsIXULWindow* GetXULWindow() { return nullptr; }
+  virtual nsIXULWindow* GetXULWindow();
 
   /**
    * If this listener is for an nsView, return it.
    */
-  virtual nsView* GetView() { return nullptr; }
+  virtual nsView* GetView();
 
   /**
    * Return the presshell for this widget listener.
    */
-  virtual nsIPresShell* GetPresShell() { return nullptr; }
+  virtual nsIPresShell* GetPresShell();
 
   /**
    * Called when a window is moved to location (x, y). Returns true if the
    * notification was handled. Coordinates are outer window screen coordinates.
    */
-  virtual bool WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY) { return false; }
+  virtual bool WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY);
 
   /**
    * Called when a window is resized to (width, height). Returns true if the
    * notification was handled. Coordinates are outer window screen coordinates.
    */
-  virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) { return false; }
+  virtual bool WindowResized(nsIWidget* aWidget,
+                             int32_t aWidth, int32_t aHeight);
 
   /**
    * Called when the size mode (minimized, maximized, fullscreen) is changed.
    */
-  virtual void SizeModeChanged(nsSizeMode sizeMode) { }
+  virtual void SizeModeChanged(nsSizeMode aSizeMode);
 
   /**
    * Called when the z-order of the window is changed. Returns true if the
    * notification was handled. aPlacement indicates the new z order. If
    * placement is nsWindowZRelative, then aRequestBelow should be the
    * window to place below. On return, aActualBelow will be set to the
    * window actually behind. This generally only applies to Windows.
    */
-  virtual bool ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
-                             nsIWidget* aRequestBelow, nsIWidget** aActualBelow) { return false; }
+  virtual bool ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
+                             nsIWidget* aRequestBelow,
+                             nsIWidget** aActualBelow);
 
   /**
    * Called when the window is activated and focused.
    */
-  virtual void WindowActivated() { }
+  virtual void WindowActivated();
 
   /**
    * Called when the window is deactivated and no longer focused.
    */
-  virtual void WindowDeactivated() { }
+  virtual void WindowDeactivated();
 
   /**
    * Called when the show/hide toolbar button on the Mac titlebar is pressed.
    */
-  virtual void OSToolbarButtonPressed() { }
+  virtual void OSToolbarButtonPressed();
 
   /**
    * Called when a request is made to close the window. Returns true if the
    * notification was handled. Returns true if the notification was handled.
    */
-  virtual bool RequestWindowClose(nsIWidget* aWidget) { return false; }
+  virtual bool RequestWindowClose(nsIWidget* aWidget);
 
   /*
    * Indicate that a paint is about to occur on this window. This is called
    * at a time when it's OK to change the geometry of this widget or of
    * other widgets. Must be called before every call to PaintWindow.
    */
-  virtual void WillPaintWindow(nsIWidget* aWidget) { }
+  virtual void WillPaintWindow(nsIWidget* aWidget);
 
   /**
    * Paint the specified region of the window. Returns true if the
    * notification was handled.
    * This is called at a time when it is not OK to change the geometry of
    * this widget or of other widgets.
    */
-  virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) { return false; }
+  virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion);
 
   /**
    * Indicates that a paint occurred.
    * This is called at a time when it is OK to change the geometry of
    * this widget or of other widgets.
    * Must be called after every call to PaintWindow.
    */
-  virtual void DidPaintWindow() { }
+  virtual void DidPaintWindow();
 
   /**
    * Request that layout schedules a repaint on the next refresh driver tick.
    */
-  virtual void RequestRepaint() { }
+  virtual void RequestRepaint();
 
   /**
    * Handle an event.
    */
   virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent,
-                                    bool aUseAttachedEvents)
-  {
-    return nsEventStatus_eIgnore;
-  }
+                                    bool aUseAttachedEvents);
 };
 
 #endif
--- a/widget/nsIWinMetroUtils.idl
+++ b/widget/nsIWinMetroUtils.idl
@@ -7,35 +7,26 @@
 
 /**
  * Integration with the "Metro"/"Modern" UI environment in Windows 8.
  *
  * Note: browser/metro/base/content/browser-scripts.js contains a stub
  * implementation of this interface for non-Windows systems, for testing and
  * development purposes only.
  */
-[scriptable, uuid(25524bde-8b30-4b49-8d67-7070c790aada)]
+[scriptable, uuid(496b4450-5757-40f7-aeb9-a958ae86dbd1)]
 interface nsIWinMetroUtils : nsISupports
 {
-  /* return constants for the handPreference property */
-  const long handPreferenceLeft = 0;
-  const long handPreferenceRight = 1;
-
   /**
    * Determine if the current browser is running in the metro immersive
    * environment.
    */
   readonly attribute boolean immersive;
 
   /**
-   * Determine if the user prefers left handed or right handed input.
-   */
-  readonly attribute long handPreference;
-
-  /**
    * Determine the activation URI
    */
   readonly attribute AString activationURI;
 
    /**
     * Show the settings flyout
     */
    void showSettingsFlyout();
--- a/widget/qt/nsWindow.cpp
+++ b/widget/qt/nsWindow.cpp
@@ -2803,16 +2803,24 @@ nsWindow::GetDPI()
     if (heightInches < 0.25) {
         // Something's broken, but we'd better not crash.
         return 96.0f;
     }
 
     return float(rootWindow->height()/heightInches);
 }
 
+nsEventStatus
+nsWindow::DispatchEvent(WidgetGUIEvent* aEvent)
+{
+    nsEventStatus status;
+    DispatchEvent(aEvent, status);
+    return status;
+}
+
 void
 nsWindow::DispatchActivateEvent(void)
 {
     if (mWidgetListener)
       mWidgetListener->WindowActivated();
 }
 
 void
@@ -3165,8 +3173,42 @@ nsWindow::GetGLFrameBufferFormat()
 {
     if (mLayerManager &&
         mLayerManager->GetBackendType() == mozilla::layers::LAYERS_OPENGL) {
         return MozQGLWidgetWrapper::isRGBAContext() ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
     }
     return LOCAL_GL_NONE;
 }
 
+void
+nsWindow::ProcessMotionEvent()
+{
+    if (mPinchEvent.needDispatch) {
+        double distance = DistanceBetweenPoints(mPinchEvent.centerPoint,
+                                                mPinchEvent.touchPoint);
+        distance *= 2;
+        mPinchEvent.delta = distance - mPinchEvent.prevDistance;
+        nsIntPoint centerPoint(mPinchEvent.centerPoint.x(),
+                               mPinchEvent.centerPoint.y());
+        DispatchGestureEvent(NS_SIMPLE_GESTURE_MAGNIFY_UPDATE,
+                             0, mPinchEvent.delta, centerPoint);
+        mPinchEvent.prevDistance = distance;
+    }
+    if (mMoveEvent.needDispatch) {
+        WidgetMouseEvent event(true, NS_MOUSE_MOVE, this,
+                               WidgetMouseEvent::eReal);
+
+        event.refPoint.x = nscoord(mMoveEvent.pos.x());
+        event.refPoint.y = nscoord(mMoveEvent.pos.y());
+
+        event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier,
+                                 mMoveEvent.modifiers & Qt::AltModifier,
+                                 mMoveEvent.modifiers & Qt::ShiftModifier,
+                                 mMoveEvent.modifiers & Qt::MetaModifier);
+        event.clickCount      = 0;
+
+        DispatchEvent(&event);
+        mMoveEvent.needDispatch = false;
+    }
+
+    mTimerStarted = false;
+}
+
--- a/widget/qt/nsWindow.h
+++ b/widget/qt/nsWindow.h
@@ -11,17 +11,17 @@
 #include <QKeyEvent>
 #include <QGestureEvent>
 #include <qgraphicswidget.h>
 #include <QTime>
 
 #include "nsAutoPtr.h"
 
 #include "nsBaseWidget.h"
-#include "mozilla/MouseEvents.h"
+#include "mozilla/EventForwards.h"
 
 #include "nsWeakReference.h"
 
 #include "nsGkAtoms.h"
 #include "nsIIdleServiceInternal.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 
@@ -178,22 +178,17 @@ public:
     // event handling code
 
     void DispatchActivateEvent(void);
     void DispatchDeactivateEvent(void);
     void DispatchActivateEventOnTopLevelWindow(void);
     void DispatchDeactivateEventOnTopLevelWindow(void);
     void DispatchResizeEvent(nsIntRect &aRect, nsEventStatus &aStatus);
 
-    nsEventStatus DispatchEvent(mozilla::WidgetGUIEvent* aEvent)
-    {
-        nsEventStatus status;
-        DispatchEvent(aEvent, status);
-        return status;
-    }
+    nsEventStatus DispatchEvent(mozilla::WidgetGUIEvent* aEvent);
 
     // Some of the nsIWidget methods
     virtual bool IsEnabled() const;
 
     // called when we are destroyed
     void OnDestroy(void);
 
     // called to check and see if a widget's dimensions are sane
@@ -373,45 +368,17 @@ private:
         *flag &= ~mask;
     }
     int32_t mQCursor;
 
     // Call this function when the users activity is the direct cause of an
     // event (like a keypress or mouse click).
     void UserActivity();
 
-    inline void ProcessMotionEvent() {
-        if (mPinchEvent.needDispatch) {
-            double distance = DistanceBetweenPoints(mPinchEvent.centerPoint, mPinchEvent.touchPoint);
-            distance *= 2;
-            mPinchEvent.delta = distance - mPinchEvent.prevDistance;
-            nsIntPoint centerPoint(mPinchEvent.centerPoint.x(), mPinchEvent.centerPoint.y());
-            DispatchGestureEvent(NS_SIMPLE_GESTURE_MAGNIFY_UPDATE,
-                                 0, mPinchEvent.delta, centerPoint);
-            mPinchEvent.prevDistance = distance;
-        }
-        if (mMoveEvent.needDispatch) {
-            WidgetMouseEvent event(true, NS_MOUSE_MOVE, this,
-                                   WidgetMouseEvent::eReal);
-
-            event.refPoint.x = nscoord(mMoveEvent.pos.x());
-            event.refPoint.y = nscoord(mMoveEvent.pos.y());
-
-            event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier,
-                                     mMoveEvent.modifiers & Qt::AltModifier,
-                                     mMoveEvent.modifiers & Qt::ShiftModifier,
-                                     mMoveEvent.modifiers & Qt::MetaModifier);
-            event.clickCount      = 0;
-
-            DispatchEvent(&event);
-            mMoveEvent.needDispatch = false;
-        }
-
-        mTimerStarted = false;
-    }
+    inline void ProcessMotionEvent();
 
     void DispatchMotionToMainThread() {
         if (!mTimerStarted) {
             nsCOMPtr<nsIRunnable> event =
                 NS_NewRunnableMethod(this, &nsWindow::ProcessMotionEvent);
             NS_DispatchToMainThread(event);
             mTimerStarted = true;
         }
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -90,16 +90,48 @@ public:
   PRUnichar GetCompositeChar(PRUnichar aBaseChar) const;
 };
 
 
 /*****************************************************************************
  * mozilla::widget::ModifierKeyState
  *****************************************************************************/
 
+ModifierKeyState::ModifierKeyState()
+{
+  Update();
+}
+
+ModifierKeyState::ModifierKeyState(bool aIsShiftDown,
+                                   bool aIsControlDown,
+                                   bool aIsAltDown)
+{
+  Update();
+  Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH);
+  Modifiers modifiers = 0;
+  if (aIsShiftDown) {
+    modifiers |= MODIFIER_SHIFT;
+  }
+  if (aIsControlDown) {
+    modifiers |= MODIFIER_CONTROL;
+  }
+  if (aIsAltDown) {
+    modifiers |= MODIFIER_ALT;
+  }
+  if (modifiers) {
+    Set(modifiers);
+  }
+}
+
+ModifierKeyState::ModifierKeyState(Modifiers aModifiers) :
+  mModifiers(aModifiers)
+{
+  EnsureAltGr();
+}
+
 void
 ModifierKeyState::Update()
 {
   mModifiers = 0;
   if (IS_VK_DOWN(VK_SHIFT)) {
     mModifiers |= MODIFIER_SHIFT;
   }
   if (IS_VK_DOWN(VK_CONTROL)) {
@@ -120,16 +152,32 @@ ModifierKeyState::Update()
   if (::GetKeyState(VK_SCROLL) & 1) {
     mModifiers |= MODIFIER_SCROLLLOCK;
   }
 
   EnsureAltGr();
 }
 
 void
+ModifierKeyState::Unset(Modifiers aRemovingModifiers)
+{
+  mModifiers &= ~aRemovingModifiers;
+  // Note that we don't need to unset AltGr flag here automatically.
+  // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
+  // checked in nsEditor, so, it can be kept.
+}
+
+void
+ModifierKeyState::Set(Modifiers aAddingModifiers)
+{
+  mModifiers |= aAddingModifiers;
+  EnsureAltGr();
+}
+
+void
 ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const
 {
   aInputEvent.modifiers = mModifiers;
 
   switch(aInputEvent.eventStructType) {
     case NS_MOUSE_EVENT:
     case NS_MOUSE_SCROLL_EVENT:
     case NS_WHEEL_EVENT:
@@ -165,16 +213,82 @@ ModifierKeyState::InitMouseEvent(WidgetI
   if (::GetKeyState(VK_XBUTTON1) < 0) {
     mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag;
   }
   if (::GetKeyState(VK_XBUTTON2) < 0) {
     mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag;
   }
 }
 
+bool
+ModifierKeyState::IsShift() const
+{
+  return (mModifiers & MODIFIER_SHIFT) != 0;
+}
+
+bool
+ModifierKeyState::IsControl() const
+{
+  return (mModifiers & MODIFIER_CONTROL) != 0;
+}
+
+bool
+ModifierKeyState::IsAlt() const
+{
+  return (mModifiers & MODIFIER_ALT) != 0;
+}
+
+bool
+ModifierKeyState::IsAltGr() const
+{
+  return IsControl() && IsAlt();
+}
+
+bool
+ModifierKeyState::IsWin() const
+{
+  return (mModifiers & MODIFIER_OS) != 0;
+}
+
+bool
+ModifierKeyState::IsCapsLocked() const
+{
+  return (mModifiers & MODIFIER_CAPSLOCK) != 0;
+}
+
+bool
+ModifierKeyState::IsNumLocked() const
+{
+  return (mModifiers & MODIFIER_NUMLOCK) != 0;
+}
+
+bool
+ModifierKeyState::IsScrollLocked() const
+{
+  return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
+}
+
+Modifiers
+ModifierKeyState::GetModifiers() const
+{
+  return mModifiers;
+}
+
+void
+ModifierKeyState::EnsureAltGr()
+{
+  // If both Control key and Alt key are pressed, it means AltGr is pressed.
+  // Ideally, we should check whether the current keyboard layout has AltGr
+  // or not.  However, setting AltGr flags for keyboard which doesn't have
+  // AltGr must not be serious bug.  So, it should be OK for now.
+  if (IsAltGr()) {
+    mModifiers |= MODIFIER_ALTGRAPH;
+  }
+}
+
 /*****************************************************************************
  * mozilla::widget::UniCharsAndModifiers
  *****************************************************************************/
 
 void
 UniCharsAndModifiers::Append(PRUnichar aUniChar, Modifiers aModifiers)
 {
   MOZ_ASSERT(mLength < 5);
@@ -231,16 +345,60 @@ UniCharsAndModifiers::operator+(const Un
   result += aOther;
   return result;
 }
 
 /*****************************************************************************
  * mozilla::widget::VirtualKey
  *****************************************************************************/
 
+// static
+VirtualKey::ShiftState
+VirtualKey::ModifiersToShiftState(Modifiers aModifiers)
+{
+  ShiftState state = 0;
+  if (aModifiers & MODIFIER_SHIFT) {
+    state |= STATE_SHIFT;
+  }
+  if (aModifiers & MODIFIER_CONTROL) {
+    state |= STATE_CONTROL;
+  }
+  if (aModifiers & MODIFIER_ALT) {
+    state |= STATE_ALT;
+  }
+  if (aModifiers & MODIFIER_CAPSLOCK) {
+    state |= STATE_CAPSLOCK;
+  }
+  return state;
+}
+
+// static
+Modifiers
+VirtualKey::ShiftStateToModifiers(ShiftState aShiftState)
+{
+  Modifiers modifiers = 0;
+  if (aShiftState & STATE_SHIFT) {
+    modifiers |= MODIFIER_SHIFT;
+  }
+  if (aShiftState & STATE_CONTROL) {
+    modifiers |= MODIFIER_CONTROL;
+  }
+  if (aShiftState & STATE_ALT) {
+    modifiers |= MODIFIER_ALT;
+  }
+  if (aShiftState & STATE_CAPSLOCK) {
+    modifiers |= MODIFIER_CAPSLOCK;
+  }
+  if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) ==
+         (MODIFIER_ALT | MODIFIER_CONTROL)) {
+    modifiers |= MODIFIER_ALTGRAPH;
+  }
+  return modifiers;
+}
+
 inline PRUnichar
 VirtualKey::GetCompositeChar(ShiftState aShiftState, PRUnichar aBaseChar) const
 {
   return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar);
 }
 
 const DeadKeyTable*
 VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
@@ -748,16 +906,22 @@ PRUnichar
 NativeKey::ComputeUnicharFromScanCode() const
 {
   return static_cast<PRUnichar>(
            ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(),
                              MAPVK_VK_TO_CHAR, mKeyboardLayout));
 }
 
 void
+NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const
+{
+  InitKeyEvent(aKeyEvent, mModKeyState);
+}
+
+void
 NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                         const ModifierKeyState& aModKeyState) const
 {
   nsIntPoint point(0, 0);
   mWidget->InitEvent(aKeyEvent, &point);
 
   switch (aKeyEvent.message) {
     case NS_KEY_DOWN:
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -6,16 +6,17 @@
 #ifndef KeyboardLayout_h__
 #define KeyboardLayout_h__
 
 #include "nscore.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsWindowBase.h"
 #include "nsWindowDefs.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include <windows.h>
 
 #define NS_NUM_OF_KEYS          70
 
 #define VK_OEM_1                0xBA   // ';:' for US
 #define VK_OEM_PLUS             0xBB   // '+' any country
 #define VK_OEM_COMMA            0xBC
@@ -49,94 +50,46 @@ static const uint32_t sModifierKeyMap[][
   { nsIWidget::CTRL_L,    VK_CONTROL, VK_LCONTROL },
   { nsIWidget::CTRL_R,    VK_CONTROL, VK_RCONTROL },
   { nsIWidget::ALT_L,     VK_MENU,    VK_LMENU },
   { nsIWidget::ALT_R,     VK_MENU,    VK_RMENU }
 };
 
 class KeyboardLayout;
 
-class ModifierKeyState {
+class ModifierKeyState
+{
 public:
-  ModifierKeyState()
-  {
-    Update();
-  }
+  ModifierKeyState();
+  ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown);
+  ModifierKeyState(Modifiers aModifiers);
 
-  ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown)
-  {
-    Update();
-    Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH);
-    Modifiers modifiers = 0;
-    if (aIsShiftDown) {
-      modifiers |= MODIFIER_SHIFT;
-    }
-    if (aIsControlDown) {
-      modifiers |= MODIFIER_CONTROL;
-    }
-    if (aIsAltDown) {
-      modifiers |= MODIFIER_ALT;
-    }
-    if (modifiers) {
-      Set(modifiers);
-    }
-  }
+  MOZ_ALWAYS_INLINE void Update();
 
-  ModifierKeyState(Modifiers aModifiers) :
-    mModifiers(aModifiers)
-  {
-    EnsureAltGr();
-  }
-
-  void Update();
-
-  void Unset(Modifiers aRemovingModifiers)
-  {
-    mModifiers &= ~aRemovingModifiers;
-    // Note that we don't need to unset AltGr flag here automatically.
-    // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
-    // checked in nsEditor, so, it can be kept.
-  }
-
-  void Set(Modifiers aAddingModifiers)
-  {
-    mModifiers |= aAddingModifiers;
-    EnsureAltGr();
-  }
+  MOZ_ALWAYS_INLINE void Unset(Modifiers aRemovingModifiers);
+  MOZ_ALWAYS_INLINE void Set(Modifiers aAddingModifiers);
 
   void InitInputEvent(WidgetInputEvent& aInputEvent) const;
 
-  bool IsShift() const { return (mModifiers & MODIFIER_SHIFT) != 0; }
-  bool IsControl() const { return (mModifiers & MODIFIER_CONTROL) != 0; }
-  bool IsAlt() const { return (mModifiers & MODIFIER_ALT) != 0; }
-  bool IsAltGr() const { return IsControl() && IsAlt(); }
-  bool IsWin() const { return (mModifiers & MODIFIER_OS) != 0; }
+  MOZ_ALWAYS_INLINE bool IsShift() const;
+  MOZ_ALWAYS_INLINE bool IsControl() const;
+  MOZ_ALWAYS_INLINE bool IsAlt() const;
+  MOZ_ALWAYS_INLINE bool IsAltGr() const;
+  MOZ_ALWAYS_INLINE bool IsWin() const;
 
-  bool IsCapsLocked() const { return (mModifiers & MODIFIER_CAPSLOCK) != 0; }
-  bool IsNumLocked() const { return (mModifiers & MODIFIER_NUMLOCK) != 0; }
-  bool IsScrollLocked() const
-  {
-    return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
-  }
+  MOZ_ALWAYS_INLINE bool IsCapsLocked() const;
+  MOZ_ALWAYS_INLINE bool IsNumLocked() const;
+  MOZ_ALWAYS_INLINE bool IsScrollLocked() const;
 
-  Modifiers GetModifiers() const { return mModifiers; }
+  MOZ_ALWAYS_INLINE Modifiers GetModifiers() const;
 
 private:
   Modifiers mModifiers;
 
-  void EnsureAltGr()
-  {
-    // If both Control key and Alt key are pressed, it means AltGr is pressed.
-    // Ideally, we should check whether the current keyboard layout has AltGr
-    // or not.  However, setting AltGr flags for keyboard which doesn't have
-    // AltGr must not be serious bug.  So, it should be OK for now.
-    if (IsAltGr()) {
-      mModifiers |= MODIFIER_ALTGRAPH;
-    }
-  }
+  MOZ_ALWAYS_INLINE void EnsureAltGr();
 
   void InitMouseEvent(WidgetInputEvent& aMouseEvent) const;
 };
 
 struct UniCharsAndModifiers
 {
   // Dead-key + up to 4 characters
   PRUnichar mChars[5];
@@ -191,55 +144,18 @@ public:
     STATE_SHIFT    = 0x01,
     STATE_CONTROL  = 0x02,
     STATE_ALT      = 0x04,
     STATE_CAPSLOCK = 0x08
   };
 
   typedef uint8_t ShiftState;
 
-  static ShiftState ModifiersToShiftState(Modifiers aModifiers)
-  {
-    ShiftState state = 0;
-    if (aModifiers & MODIFIER_SHIFT) {
-      state |= STATE_SHIFT;
-    }
-    if (aModifiers & MODIFIER_CONTROL) {
-      state |= STATE_CONTROL;
-    }
-    if (aModifiers & MODIFIER_ALT) {
-      state |= STATE_ALT;
-    }
-    if (aModifiers & MODIFIER_CAPSLOCK) {
-      state |= STATE_CAPSLOCK;
-    }
-    return state;
-  }
-
-  static Modifiers ShiftStateToModifiers(ShiftState aShiftState)
-  {
-    Modifiers modifiers = 0;
-    if (aShiftState & STATE_SHIFT) {
-      modifiers |= MODIFIER_SHIFT;
-    }
-    if (aShiftState & STATE_CONTROL) {
-      modifiers |= MODIFIER_CONTROL;
-    }
-    if (aShiftState & STATE_ALT) {
-      modifiers |= MODIFIER_ALT;
-    }
-    if (aShiftState & STATE_CAPSLOCK) {
-      modifiers |= MODIFIER_CAPSLOCK;
-    }
-    if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) ==
-           (MODIFIER_ALT | MODIFIER_CONTROL)) {
-      modifiers |= MODIFIER_ALTGRAPH;
-    }
-    return modifiers;
-  }
+  static ShiftState ModifiersToShiftState(Modifiers aModifiers);
+  static Modifiers ShiftStateToModifiers(ShiftState aShiftState);
 
 private:
   union KeyShiftState
   {
     struct
     {
       PRUnichar Chars[4];
     } Normal;
@@ -423,20 +339,17 @@ private:
    */
   PRUnichar ComputeUnicharFromScanCode() const;
 
   /**
    * Initializes the aKeyEvent with the information stored in the instance.
    */
   void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                     const ModifierKeyState& aModKeyState) const;
-  void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const
-  {
-    InitKeyEvent(aKeyEvent, mModKeyState);
-  }
+  void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const;
 
   /**
    * Dispatches the key event.  Returns true if the event is consumed.
    * Otherwise, false.
    */
   bool DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                         const MSG* aMsgSentToPlugin = nullptr) const;
 
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -56,16 +56,17 @@ CPP_SOURCES += [
     'nsScreenManagerWin.cpp',
     'nsScreenWin.cpp',
     'nsSound.cpp',
     'nsToolkit.cpp',
     'nsUXThemeData.cpp',
     'nsWidgetFactory.cpp',
     'nsWinGesture.cpp',
     'nsWindow.cpp',
+    'nsWindowBase.cpp',
     'nsWindowDbg.cpp',
     'nsWindowGfx.cpp',
 ]
 
 if CONFIG['MOZ_CRASHREPORTER']:
     CPP_SOURCES += [
         'LSPAnnotator.cpp',
     ]
--- a/widget/windows/nsTextStore.h
+++ b/widget/windows/nsTextStore.h
@@ -8,17 +8,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsITimer.h"
 #include "nsIWidget.h"
 #include "nsWindowBase.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/TextEvents.h"
+#include "mozilla/TextRange.h"
 
 #include <msctf.h>
 #include <textstor.h>
 
 // GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID.
 // With initguid.h, we get its instance instead of extern declaration.
 #ifdef INPUTSCOPE_INIT_GUID
 #include <initguid.h>
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -167,16 +167,18 @@
 #include "nsCrashOnException.h"
 #include "nsIXULRuntime.h"
 
 #include "nsIContent.h"
 
 #include "mozilla/HangMonitor.h"
 #include "WinIMEHandler.h"
 
+#include "npapi.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 /**************************************************************
  **************************************************************
  **
new file mode 100644
--- /dev/null
+++ b/widget/windows/nsWindowBase.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsWindowBase.h"
+
+#include "mozilla/MiscEvents.h"
+#include "npapi.h"
+
+using namespace mozilla;
+
+bool
+nsWindowBase::DispatchPluginEvent(const MSG& aMsg)
+{
+  if (!PluginHasFocus()) {
+    return false;
+  }
+  WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this);
+  nsIntPoint point(0, 0);
+  InitEvent(pluginEvent, &point);
+  NPEvent npEvent;
+  npEvent.event = aMsg.message;
+  npEvent.wParam = aMsg.wParam;
+  npEvent.lParam = aMsg.lParam;
+  pluginEvent.pluginEvent = &npEvent;
+  pluginEvent.retargetToFocusedDocument = true;
+  return DispatchWindowEvent(&pluginEvent);
+}
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -1,19 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; 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 nsWindowBase_h_
 #define nsWindowBase_h_
 
-#include "mozilla/MiscEvents.h"
+#include "mozilla/EventForwards.h"
 #include "nsBaseWidget.h"
-#include "npapi.h"
 #include <windows.h>
 
 /*
  * nsWindowBase - Base class of common methods other classes need to access
  * in both win32 and winrt window classes.
  */
 class nsWindowBase : public nsBaseWidget
 {
@@ -54,32 +53,17 @@ public:
    * is called by KeyboardLayout to dispatch gecko events.
    * Returns true if it's consumed.  Otherwise, false.
    */
   virtual bool DispatchKeyboardEvent(mozilla::WidgetGUIEvent* aEvent) = 0;
 
   /*
    * Default dispatch of a plugin event.
    */
-  virtual bool DispatchPluginEvent(const MSG &aMsg)
-  {
-    if (!PluginHasFocus()) {
-      return false;
-    }
-    mozilla::WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this);
-    nsIntPoint point(0, 0);
-    InitEvent(pluginEvent, &point);
-    NPEvent npEvent;
-    npEvent.event = aMsg.message;
-    npEvent.wParam = aMsg.wParam;
-    npEvent.lParam = aMsg.lParam;
-    pluginEvent.pluginEvent = (void *)&npEvent;
-    pluginEvent.retargetToFocusedDocument = true;
-    return DispatchWindowEvent(&pluginEvent);
-  }
+  virtual bool DispatchPluginEvent(const MSG& aMsg);
 
   /*
    * Returns true if a plugin has focus on this widget.  Otherwise, false.
    */
   virtual bool PluginHasFocus() const MOZ_FINAL
   {
     return (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
   }
--- a/widget/windows/winrt/nsWinMetroUtils.cpp
+++ b/widget/windows/winrt/nsWinMetroUtils.cpp
@@ -380,37 +380,16 @@ NS_IMETHODIMP
 nsWinMetroUtils::GetImmersive(bool *aImersive)
 {
   *aImersive =
     XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWinMetroUtils::GetHandPreference(int32_t *aHandPreference)
-{
-  if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
-    *aHandPreference = nsIWinMetroUtils::handPreferenceRight;
-    return NS_OK;
-  }
-
-  ComPtr<IUISettings> uiSettings;
-  AssertRetHRESULT(ActivateGenericInstance(RuntimeClass_Windows_UI_ViewManagement_UISettings, uiSettings), NS_ERROR_UNEXPECTED);
-
-  HandPreference value;
-  uiSettings->get_HandPreference(&value);
-  if (value == HandPreference::HandPreference_LeftHanded)
-    *aHandPreference = nsIWinMetroUtils::handPreferenceLeft;
-  else
-    *aHandPreference = nsIWinMetroUtils::handPreferenceRight;
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsWinMetroUtils::GetActivationURI(nsAString &aActivationURI)
 {
   if (!sFrameworkView) {
     NS_WARNING("GetActivationURI used before view is created!");
     return NS_OK;
   }
   sFrameworkView->GetActivationURI(aActivationURI);
   return NS_OK;
--- a/widget/xpwidgets/moz.build
+++ b/widget/xpwidgets/moz.build
@@ -24,16 +24,17 @@ CPP_SOURCES += [
     'nsBaseDragService.cpp',
     'nsBaseScreen.cpp',
     'nsBaseWidget.cpp',
     'nsClipboardHelper.cpp',
     'nsClipboardPrivacyHandler.cpp',
     'nsFilePickerProxy.cpp',
     'nsHTMLFormatConverter.cpp',
     'nsIdleService.cpp',
+    'nsIWidgetListener.cpp',
     'nsPrimitiveHelpers.cpp',
     'nsPrintOptionsImpl.cpp',
     'nsPrintSession.cpp',
     'nsPrintSettingsImpl.cpp',
     'nsTransferable.cpp',
     'nsXPLookAndFeel.cpp',
 ]
 
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/nsIWidgetListener.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIWidgetListener.h"
+
+#include "nsRegion.h"
+#include "nsView.h"
+#include "nsIPresShell.h"
+#include "nsIWidget.h"
+#include "nsIXULWindow.h"
+
+#include "mozilla/BasicEvents.h"
+
+using namespace mozilla;
+
+nsIXULWindow*
+nsIWidgetListener::GetXULWindow()
+{
+  return nullptr;
+}
+
+nsView*
+nsIWidgetListener::GetView()
+{
+  return nullptr;
+}
+
+nsIPresShell*
+nsIWidgetListener::GetPresShell()
+{
+  return nullptr;
+}
+
+bool
+nsIWidgetListener::WindowMoved(nsIWidget* aWidget,
+                               int32_t aX,
+                               int32_t aY)
+{
+  return false;
+}
+
+bool
+nsIWidgetListener::WindowResized(nsIWidget* aWidget,
+                                 int32_t aWidth,
+                                 int32_t aHeight)
+{
+  return false;
+}
+
+void
+nsIWidgetListener::SizeModeChanged(nsSizeMode aSizeMode)
+{
+}
+
+bool
+nsIWidgetListener::ZLevelChanged(bool aImmediate,
+                                 nsWindowZ* aPlacement,
+                                 nsIWidget* aRequestBelow,
+                                 nsIWidget** aActualBelow)
+{
+  return false;
+}
+
+void
+nsIWidgetListener::WindowActivated()
+{
+}
+
+void
+nsIWidgetListener::WindowDeactivated()
+{
+}
+
+void
+nsIWidgetListener::OSToolbarButtonPressed()
+{
+}
+
+bool
+nsIWidgetListener::RequestWindowClose(nsIWidget* aWidget)
+{
+  return false;
+}
+
+void
+nsIWidgetListener::WillPaintWindow(nsIWidget* aWidget)
+{
+}
+
+bool
+nsIWidgetListener::PaintWindow(nsIWidget* aWidget,
+                               nsIntRegion aRegion)
+{
+  return false;
+}
+
+void
+nsIWidgetListener::DidPaintWindow()
+{
+}
+
+void
+nsIWidgetListener::RequestRepaint()
+{
+}
+
+nsEventStatus
+nsIWidgetListener::HandleEvent(WidgetGUIEvent* aEvent,
+                               bool aUseAttachedEvents)
+{
+  return nsEventStatus_eIgnore;
+}
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -1,22 +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 "nsISupports.idl"
 
-interface nsISimpleEnumerator;
+interface nsICancelableRunnable;
+interface nsIDOMWindow;
 interface nsIRunnable;
-interface nsICancelableRunnable;
+interface nsISimpleEnumerator;
 
 /*
- * Memory reporters measure Firefox's memory usage.  They are mainly used to
+ * Memory reporters measure Firefox's memory usage.  They are primarily used to
  * generate the about:memory page.  You should read
  * https://wiki.mozilla.org/Memory_Reporting before writing a memory
  * reporter.
  */
 
 [scriptable, function, uuid(3a61be3b-b93b-461a-a4f8-388214f558b1)]
 interface nsIMemoryReporterCallback : nsISupports
 {
@@ -303,22 +304,42 @@ interface nsIMemoryReporterManager : nsI
   [infallible] readonly attribute boolean hasMozMallocUsableSize;
 
   /*
    * Run a series of GC/CC's in an attempt to minimize the application's memory
    * usage.  When we're finished, we invoke the given runnable if it's not
    * null.  Returns a reference to the runnable used for carrying out the task.
    */
   nsICancelableRunnable minimizeMemoryUsage(in nsIRunnable callback);
+
+  /*
+   * Measure the memory that is known to be owned by this tab, split up into
+   * several broad categories.  Note that this will be an underestimate of the
+   * true number, due to imperfect memory reporter coverage (corresponding to
+   * about:memory's "heap-unclassified"), and due to some memory shared between
+   * tabs not being counted.
+   *
+   * The time taken for the measurement (split into JS and non-JS parts) is
+   * also returned.
+   */
+  void sizeOfTab(in nsIDOMWindow window,
+                 out int64_t jsObjectsSize, out int64_t jsStringsSize,
+                 out int64_t jsOtherSize, out int64_t domSize,
+                 out int64_t styleSize, out int64_t otherSize,
+                 out int64_t totalSize,
+                 out double jsMilliseconds, out double nonJSMilliseconds);
 };
 
 %{C++
 
+#include "js/TypeDecls.h"
 #include "nsStringGlue.h"
 
+class nsPIDOMWindow;
+
 // Note that the memory reporters are held in an nsCOMArray, which means
 // that individual reporters should be referenced with |nsIMemoryReporter *|
 // instead of nsCOMPtr<nsIMemoryReporter>.
 
 XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter);
 XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter);
 
 namespace mozilla {
@@ -350,16 +371,31 @@ DECL_UNREGISTER_DISTINGUISHED_AMOUNT(Sto
 DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
 DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
 
 DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
 
 #undef DECL_REGISTER_DISTINGUISHED_AMOUNT
 #undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
 
+// Likewise for per-tab measurement.
+
+typedef nsresult (*JSSizeOfTabFn)(JSObject* aObj,
+                                  size_t* aJsObjectsSize,
+                                  size_t* aJsStringSize,
+                                  size_t* aJsPrivateSize,
+                                  size_t* aJsOtherSize);
+typedef nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindow* aWindow,
+                                     size_t* aDomSize,
+                                     size_t* aStyleSize,
+                                     size_t* aOtherSize);
+
+nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
+nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
+
 }
 
 #if defined(MOZ_DMD)
 namespace mozilla {
 namespace dmd {
 // This runs all the memory reporters but does nothing with the results;  i.e.
 // it does the minimal amount of work possible for DMD to do its thing.
 void RunReporters();
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -605,29 +605,30 @@ namespace mozilla {
 
 #define DUMP(o, s) \
   do { \
     nsresult rv = (o)->Write(s); \
     NS_ENSURE_SUCCESS(rv, rv); \
   } while (0)
 
 static nsresult
-DumpReport(nsIGZFileWriter *aWriter, bool aIsFirst,
+DumpReport(nsIGZFileWriter *aWriter, bool *aIsFirstPtr,
   const nsACString &aProcess, const nsACString &aPath, int32_t aKind,
   int32_t aUnits, int64_t aAmount, const nsACString &aDescription)
 {
-  DUMP(aWriter, aIsFirst ? "[" : ",");
-
   // We only want to dump reports for this process.  If |aProcess| is
   // non-nullptr that means we've received it from another process in response
   // to a "child-memory-reporter-request" event;  ignore such reports.
   if (!aProcess.IsEmpty()) {
     return NS_OK;
   }
 
+  DUMP(aWriter, *aIsFirstPtr ? "[" : ",");
+  *aIsFirstPtr = false;
+
   // Generate the process identifier, which is of the form "$PROCESS_NAME
   // (pid $PID)", or just "(pid $PID)" if we don't have a process name.  If
   // we're the main process, we let $PROCESS_NAME be "Main Process".
   nsAutoCString processId;
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // We're the main process.
     processId.AssignLiteral("Main Process ");
   } else if (ContentChild *cc = ContentChild::GetSingleton()) {
@@ -673,37 +674,32 @@ DumpReport(nsIGZFileWriter *aWriter, boo
   return NS_OK;
 }
 
 class DumpReporterCallback MOZ_FINAL : public nsIMemoryReporterCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
-  DumpReporterCallback(bool* aIsFirstPtr) : mIsFirstPtr(aIsFirstPtr) {}
+  DumpReporterCallback() : mIsFirst(true) {}
 
   NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
       int32_t aKind, int32_t aUnits, int64_t aAmount,
       const nsACString &aDescription,
       nsISupports *aData)
   {
     nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
     NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
 
-    // The |isFirst = false| assumes that at least one single reporter is
-    // present and so will have been processed in
-    // DumpProcessMemoryReportsToGZFileWriter() below.
-    bool isFirst = *mIsFirstPtr;
-    *mIsFirstPtr = false;
-    return DumpReport(writer, isFirst, aProcess, aPath, aKind, aUnits, aAmount,
-                      aDescription);
+    return DumpReport(writer, &mIsFirst, aProcess, aPath, aKind, aUnits,
+                      aAmount, aDescription);
   }
 
 private:
-  bool* mIsFirstPtr;
+  bool mIsFirst;
 };
 
 NS_IMPL_ISUPPORTS1(DumpReporterCallback, nsIMemoryReporterCallback)
 
 } // namespace mozilla
 
 static void
 MakeFilename(const char *aPrefix, const nsAString &aIdentifier,
@@ -812,21 +808,20 @@ DumpProcessMemoryReportsToGZFileWriter(n
     do_GetService("@mozilla.org/memory-reporter-manager;1");
   NS_ENSURE_STATE(mgr);
 
   DUMP(aWriter, mgr->GetHasMozMallocUsableSize() ? "true" : "false");
   DUMP(aWriter, ",\n");
   DUMP(aWriter, "  \"reports\": ");
 
   // Process reporters.
-  bool isFirst = true;
   bool more;
   nsCOMPtr<nsISimpleEnumerator> e;
   mgr->EnumerateReporters(getter_AddRefs(e));
-  nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback(&isFirst);
+  nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback();
   while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
     nsCOMPtr<nsIMemoryReporter> r;
     e->GetNext(getter_AddRefs(r));
     r->CollectReports(cb, aWriter);
   }
 
   DUMP(aWriter, "\n  ]\n}\n");
 
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -7,17 +7,20 @@
 #include "nsAtomTable.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemoryReporterManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
+#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
 #include "nsIObserverService.h"
+#include "nsIGlobalObject.h"
 #if defined(XP_LINUX)
 #include "nsMemoryInfoDumper.h"
 #endif
 #include "mozilla/Attributes.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 
@@ -840,16 +843,17 @@ HashtableEnumerator::GetNext(nsISupports
 
 } // anonymous namespace
 
 nsMemoryReporterManager::nsMemoryReporterManager()
   : mMutex("nsMemoryReporterManager::mMutex"),
     mIsRegistrationBlocked(false)
 {
     PodZero(&mAmountFns);
+    PodZero(&mSizeOfTabFns);
 }
 
 nsMemoryReporterManager::~nsMemoryReporterManager()
 {
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
@@ -1262,16 +1266,66 @@ nsMemoryReporterManager::MinimizeMemoryU
 
   nsRefPtr<nsICancelableRunnable> runnable =
     new MinimizeMemoryUsageRunnable(aCallback);
   NS_ADDREF(*aResult = runnable);
 
   return NS_DispatchToMainThread(runnable);
 }
 
+NS_IMETHODIMP
+nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
+                                   int64_t* aJSObjectsSize,
+                                   int64_t* aJSStringsSize,
+                                   int64_t* aJSOtherSize,
+                                   int64_t* aDomSize,
+                                   int64_t* aStyleSize,
+                                   int64_t* aOtherSize,
+                                   int64_t* aTotalSize,
+                                   double*  aJSMilliseconds,
+                                   double*  aNonJSMilliseconds)
+{
+    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
+    nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aTopWindow);
+    NS_ENSURE_TRUE(!!global && !!piWindow, NS_ERROR_FAILURE);
+
+    TimeStamp t1 = TimeStamp::Now();
+
+    // Measure JS memory consumption (and possibly some non-JS consumption, via
+    // |jsPrivateSize|).
+    size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
+    nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
+                                    &jsObjectsSize, &jsStringsSize,
+                                    &jsPrivateSize, &jsOtherSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    TimeStamp t2 = TimeStamp::Now();
+
+    // Measure non-JS memory consumption.
+    size_t domSize, styleSize, otherSize;
+    mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
+
+    TimeStamp t3 = TimeStamp::Now();
+
+    *aTotalSize = 0;
+    #define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
+    DO(aJSObjectsSize, jsObjectsSize);
+    DO(aJSStringsSize, jsStringsSize);
+    DO(aJSOtherSize,   jsOtherSize);
+    DO(aDomSize,       jsPrivateSize + domSize);
+    DO(aStyleSize,     styleSize);
+    DO(aOtherSize,     otherSize);
+    #undef DO
+
+    *aJSMilliseconds    = (t2 - t1).ToMilliseconds();
+    *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
+
+    return NS_OK;
+}
+
 // Most memory reporters don't need thread safety, but some do.  Make them all
 // thread-safe just to be safe.  Memory reporters are created and destroyed
 // infrequently enough that the performance cost should be negligible.
 NS_IMPL_ISUPPORTS1(MemoryUniReporter, nsIMemoryReporter)
 
 nsresult
 NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter)
 {
@@ -1289,44 +1343,41 @@ NS_UnregisterMemoryReporter(nsIMemoryRep
     if (!mgr) {
         return NS_ERROR_FAILURE;
     }
     return mgr->UnregisterReporter(aReporter);
 }
 
 namespace mozilla {
 
+#define GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
+    nsCOMPtr<nsIMemoryReporterManager> imgr =                                 \
+        do_GetService("@mozilla.org/memory-reporter-manager;1");              \
+    nsRefPtr<nsMemoryReporterManager> mgr =                                   \
+        static_cast<nsMemoryReporterManager*>(imgr.get());                    \
+    if (!mgr) {                                                               \
+        return NS_ERROR_FAILURE;                                              \
+    }
+
 // Macro for generating functions that register distinguished amount functions
 // with the memory reporter manager.
 #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name)                      \
     nsresult                                                                  \
     Register##name##DistinguishedAmount(kind##AmountFn aAmountFn)             \
     {                                                                         \
-        nsCOMPtr<nsIMemoryReporterManager> imgr =                             \
-            do_GetService("@mozilla.org/memory-reporter-manager;1");          \
-        nsRefPtr<nsMemoryReporterManager> mgr =                               \
-            static_cast<nsMemoryReporterManager*>(imgr.get());                \
-        if (!mgr) {                                                           \
-            return NS_ERROR_FAILURE;                                          \
-        }                                                                     \
+        GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
         mgr->mAmountFns.m##name = aAmountFn;                                  \
         return NS_OK;                                                         \
     }
 
 #define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name)                          \
     nsresult                                                                  \
     Unregister##name##DistinguishedAmount()                                   \
     {                                                                         \
-        nsCOMPtr<nsIMemoryReporterManager> imgr =                             \
-            do_GetService("@mozilla.org/memory-reporter-manager;1");          \
-        nsRefPtr<nsMemoryReporterManager> mgr =                               \
-            static_cast<nsMemoryReporterManager*>(imgr.get());                \
-        if (!mgr) {                                                           \
-            return NS_ERROR_FAILURE;                                          \
-        }                                                                     \
+        GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
         mgr->mAmountFns.m##name = nullptr;                                    \
         return NS_OK;                                                         \
     }
 
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
@@ -1339,16 +1390,32 @@ DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(S
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
 
 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
 
 #undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
 #undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
 
+#define DEFINE_REGISTER_SIZE_OF_TAB(name)                                     \
+    nsresult