Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Thu, 20 Dec 2012 00:07:49 -0800
changeset 117363 11f7569992ce60beff4943aeea34260d824c2769
parent 117362 8468249f439a465788a8a8c789b543704e540d36 (current diff)
parent 116521 21195f52311c61195b9c555279bc734626e7a1c0 (diff)
child 117364 8ca91ba7e69963fc33ba6936fe98f444bef3f894
push id24098
push userrnewman@mozilla.com
push dateThu, 03 Jan 2013 03:39:06 +0000
treeherdermozilla-central@6955309291ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
content/media/test/short.mp4
image/public/imgIContainerObserver.idl
image/public/imgIDecoderObserver.idl
js/src/builtin/array.js
--- a/accessible/src/base/AccTypes.h
+++ b/accessible/src/base/AccTypes.h
@@ -1,23 +1,28 @@
 /* -*- 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/. */
 
-#pragma once
+#ifndef mozilla_a11y_AccTypes_h
+#define mozilla_a11y_AccTypes_h
 
 namespace mozilla {
 namespace a11y {
 
 /**
- * Accessible object types used when creating an accessible based on the frame.
+ * Accessible object types. Each accessible class can have own type.
  */
 enum AccType {
+  /**
+   * This set of types is used for accessible creation, keep them together in
+   * alphabetical order since they are used in switch statement.
+   */
   eNoType,
   eHTMLBRType,
   eHTMLButtonType,
   eHTMLCanvasType,
   eHTMLCaptionType,
   eHTMLCheckboxType,
   eHTMLComboboxType,
   eHTMLFileInputType,
@@ -31,14 +36,50 @@ enum AccType {
   eHTMLRadioButtonType,
   eHTMLTableType,
   eHTMLTableCellType,
   eHTMLTableRowType,
   eHTMLTextFieldType,
   eHyperTextType,
   eImageType,
   eOuterDocType,
-  ePlugin,
-  eTextLeafType
+  ePluginType,
+  eTextLeafType,
+
+  /**
+   * Other accessible types.
+   */
+  eApplicationType,
+  eImageMapType,
+  eMenuPopupType,
+  eProgressType,
+  eRootType,
+  eXULDeckType,
+  eXULTreeType,
+
+  eLastAccType = eXULTreeType
 };
-}
-}
 
+/**
+ * Generic accessible type, different accessible classes can share the same
+ * type, the same accessible class can have several types.
+ */
+enum AccGenericType {
+  eAutoComplete = 1 << 0,
+  eAutoCompletePopup = 1 << 1,
+  eCombobox = 1 << 2,
+  eDocument = 1 << 3,
+  eHyperText = 1 << 4,
+  eList = 1 << 5,
+  eListControl = 1 << 6,
+  eMenuButton = 1 << 7,
+  eSelect = 1 << 8,
+  eTable = 1 << 9,
+  eTableCell = 1 << 10,
+  eTableRow = 1 << 11,
+
+  eLastAccGenericType = eTableRow
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_AccTypes_h
--- a/accessible/src/base/TextAttrs.cpp
+++ b/accessible/src/base/TextAttrs.cpp
@@ -360,19 +360,17 @@ TextAttrsMgr::FontFamilyTextAttr::
 bool
 TextAttrsMgr::FontFamilyTextAttr::
   GetFontFamily(nsIFrame* aFrame, nsString& aFamily)
 {
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
 
   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
-  gfxFont* font = fontGroup->GetFontAt(0);
-  gfxFontEntry* fontEntry = font->GetFontEntry();
-  aFamily = fontEntry->FamilyName();
+  aFamily = fontGroup->GetFamilyNameAt(0);
   return true;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // FontSizeTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -105,17 +105,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // columnheader
     &nsGkAtoms::columnheader,
     roles::COLUMNHEADER,
     kUseMapRole,
     eNoValue,
     eSortAction,
     eNoLiveAttr,
-    Accessible::eTableCellAccessible,
+    eTableCell,
     kNoReqStates,
     eARIASelectable,
     eARIAReadonly
   },
   { // combobox
     &nsGkAtoms::combobox,
     roles::COMBOBOX,
     kUseMapRole,
@@ -139,17 +139,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // directory
     &nsGkAtoms::directory,
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eListAccessible,
+    eList,
     kNoReqStates
   },
   { // document
     &nsGkAtoms::document,
     roles::DOCUMENT,
     kUseMapRole,
     eNoValue,
     eNoAction,
@@ -170,29 +170,29 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // grid
     &nsGkAtoms::grid,
     roles::TABLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eSelectAccessible | Accessible::eTableAccessible,
+    eSelect | eTable,
     states::FOCUSABLE,
     eARIAMultiSelectable,
     eARIAReadonly
   },
   { // gridcell
     &nsGkAtoms::gridcell,
     roles::GRID_CELL,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eTableCellAccessible,
+    eTableCell,
     kNoReqStates,
     eARIASelectable,
     eARIAReadonly
   },
   { // group
     &nsGkAtoms::group,
     roles::GROUPING,
     kUseMapRole,
@@ -234,27 +234,27 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // list
     &nsGkAtoms::list,
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eListAccessible,
+    eList,
     states::READONLY
   },
   { // listbox
     &nsGkAtoms::listbox,
     roles::LISTBOX,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eSelectAccessible,
+    eSelect,
     kNoReqStates,
     eARIAMultiSelectable,
     eARIAReadonly
   },
   { // listitem
     &nsGkAtoms::listitem,
     roles::LISTITEM,
     kUseMapRole,
@@ -424,17 +424,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // row
     &nsGkAtoms::row,
     roles::ROW,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eTableRowAccessible,
+    eTableRow,
     kNoReqStates,
     eARIASelectable
   },
   { // rowgroup
     &nsGkAtoms::rowgroup,
     roles::GROUPING,
     kUseMapRole,
     eNoValue,
@@ -445,17 +445,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // rowheader
     &nsGkAtoms::rowheader,
     roles::ROWHEADER,
     kUseMapRole,
     eNoValue,
     eSortAction,
     eNoLiveAttr,
-    Accessible::eTableCellAccessible,
+    eTableCell,
     kNoReqStates,
     eARIASelectable,
     eARIAReadonly
   },
   { // scrollbar
     &nsGkAtoms::scrollbar,
     roles::SCROLLBAR,
     kUseMapRole,
@@ -524,17 +524,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // tablist
     &nsGkAtoms::tablist,
     roles::PAGETABLIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     ePoliteLiveAttr,
-    Accessible::eSelectAccessible,
+    eSelect,
     kNoReqStates
   },
   { // tabpanel
     &nsGkAtoms::tabpanel,
     roles::PROPERTYPAGE,
     kUseMapRole,
     eNoValue,
     eNoAction,
@@ -586,29 +586,29 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // tree
     &nsGkAtoms::tree,
     roles::OUTLINE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eSelectAccessible,
+    eSelect,
     kNoReqStates,
     eARIAReadonly,
     eARIAMultiSelectable
   },
   { // treegrid
     &nsGkAtoms::treegrid,
     roles::TREE_TABLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    Accessible::eSelectAccessible | Accessible::eTableAccessible,
+    eSelect | eTable,
     kNoReqStates,
     eARIAReadonly,
     eARIAMultiSelectable
   },
   { // treeitem
     &nsGkAtoms::treeitem,
     roles::OUTLINEITEM,
     kUseMapRole,
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -289,96 +289,80 @@ nsAccUtils::GetTextAccessibleFromSelecti
 
     accessible = accessible->Parent();
   } while (accessible);
 
   NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
   return nullptr;
 }
 
-nsresult
+nsIntPoint
 nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
                                   uint32_t aCoordinateType,
-                                  nsAccessNode *aAccessNode,
-                                  nsIntPoint *aCoords)
+                                  Accessible* aAccessible)
 {
-  NS_ENSURE_ARG_POINTER(aCoords);
-
-  aCoords->MoveTo(aX, aY);
+  nsIntPoint coords(aX, aY);
 
   switch (aCoordinateType) {
     case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
       break;
 
     case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
     {
-      NS_ENSURE_ARG(aAccessNode);
-      *aCoords += GetScreenCoordsForWindow(aAccessNode);
+      coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
       break;
     }
 
     case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
     {
-      NS_ENSURE_ARG(aAccessNode);
-      *aCoords += GetScreenCoordsForParent(aAccessNode);
+      coords += GetScreenCoordsForParent(aAccessible);
       break;
     }
 
     default:
-      return NS_ERROR_INVALID_ARG;
+      NS_NOTREACHED("invalid coord type!");
   }
 
-  return NS_OK;
+  return coords;
 }
 
-nsresult
+void
 nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
                                   uint32_t aCoordinateType,
-                                  nsAccessNode *aAccessNode)
+                                  Accessible* aAccessible)
 {
   switch (aCoordinateType) {
     case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
       break;
 
     case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
     {
-      NS_ENSURE_ARG(aAccessNode);
-      nsIntPoint coords = nsAccUtils::GetScreenCoordsForWindow(aAccessNode);
+      nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
       *aX -= coords.x;
       *aY -= coords.y;
       break;
     }
 
     case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
     {
-      NS_ENSURE_ARG(aAccessNode);
-      nsIntPoint coords = nsAccUtils::GetScreenCoordsForParent(aAccessNode);
+      nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
       *aX -= coords.x;
       *aY -= coords.y;
       break;
     }
 
     default:
-      return NS_ERROR_INVALID_ARG;
+    NS_NOTREACHED("invalid coord type!");
   }
-
-  return NS_OK;
 }
 
 nsIntPoint
-nsAccUtils::GetScreenCoordsForWindow(nsAccessNode *aAccessNode)
+nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
 {
-  return nsCoreUtils::GetScreenCoordsForWindow(aAccessNode->GetNode());
-}
-
-nsIntPoint
-nsAccUtils::GetScreenCoordsForParent(nsAccessNode *aAccessNode)
-{
-  DocAccessible* document = aAccessNode->Document();
-  Accessible* parent = document->GetContainerAccessible(aAccessNode->GetNode());
+  Accessible* parent = aAccessible->Parent();
   if (!parent)
     return nsIntPoint(0, 0);
 
   nsIFrame *parentFrame = parent->GetFrame();
   if (!parentFrame)
     return nsIntPoint(0, 0);
 
   nsRect rect = parentFrame->GetScreenRectInAppUnits();
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -20,17 +20,16 @@
 #include "nsIPresShell.h"
 #include "nsPoint.h"
 
 struct nsRoleMapEntry;
 
 namespace mozilla {
 namespace a11y {
 
-class nsAccessNode;
 class Accessible;
 class HyperTextAccessible;
 class DocAccessible;
 
 class nsAccUtils
 {
 public:
   /**
@@ -161,53 +160,45 @@ public:
 
   /**
    * Converts the given coordinates to coordinates relative screen.
    *
    * @param aX               [in] the given x coord
    * @param aY               [in] the given y coord
    * @param aCoordinateType  [in] specifies coordinates origin (refer to
    *                         nsIAccessibleCoordinateType)
-   * @param aAccessNode      [in] the accessible if coordinates are given
+   * @param aAccessible      [in] the accessible if coordinates are given
    *                         relative it.
-   * @param aCoords          [out] converted coordinates
+   * @return converted coordinates
    */
-  static nsresult ConvertToScreenCoords(int32_t aX, int32_t aY,
-                                        uint32_t aCoordinateType,
-                                        nsAccessNode *aAccessNode,
-                                        nsIntPoint *aCoords);
+  static nsIntPoint ConvertToScreenCoords(int32_t aX, int32_t aY,
+                                          uint32_t aCoordinateType,
+                                          Accessible* aAccessible);
 
   /**
    * Converts the given coordinates relative screen to another coordinate
    * system.
    *
    * @param aX               [in, out] the given x coord
    * @param aY               [in, out] the given y coord
    * @param aCoordinateType  [in] specifies coordinates origin (refer to
    *                         nsIAccessibleCoordinateType)
-   * @param aAccessNode      [in] the accessible if coordinates are given
+   * @param aAccessible      [in] the accessible if coordinates are given
    *                         relative it
    */
-  static nsresult ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
-                                        uint32_t aCoordinateType,
-                                        nsAccessNode *aAccessNode);
-
-  /**
-   * Returns coordinates relative screen for the top level window.
-   *
-   * @param aAccessNode  the accessible hosted in the window
-   */
-  static nsIntPoint GetScreenCoordsForWindow(nsAccessNode *aAccessNode);
+  static void ConvertScreenCoordsTo(int32_t* aX, int32_t* aY,
+                                    uint32_t aCoordinateType,
+                                    Accessible* aAccessible);
 
   /**
    * Returns coordinates relative screen for the parent of the given accessible.
    *
-   * @param aAccessNode  the accessible
+   * @param [in] aAccessible  the accessible
    */
-  static nsIntPoint GetScreenCoordsForParent(nsAccessNode *aAccessNode);
+  static nsIntPoint GetScreenCoordsForParent(Accessible* aAccessible);
 
   /**
    * Return the role of the given accessible.
    */
   static uint32_t Role(nsIAccessible *aAcc)
   {
     uint32_t role = nsIAccessibleRole::ROLE_NOTHING;
     if (aAcc)
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -792,24 +792,24 @@ nsAccessibilityService::GetOrCreateAcces
 
     roleMapEntry = nullptr;
   }
 
   if (!newAcc && isHTML) {  // HTML accessibles
     if (roleMapEntry) {
       // Create pure ARIA grid/treegrid related accessibles if they weren't used
       // on accessible HTML table elements.
-      if ((roleMapEntry->accTypes & Accessible::eTableCellAccessible)) {
-        if (aContext->IsOfType(Accessible::eTableRowAccessible) &&
+      if ((roleMapEntry->accTypes & eTableCell)) {
+        if (aContext->IsTableRow() &&
             (frame->AccessibleType() != eHTMLTableCellType ||
              aContext->GetContent() != content->GetParent())) {
           newAcc = new ARIAGridCellAccessibleWrap(content, document);
         }
 
-      } else if ((roleMapEntry->accTypes & Accessible::eTableAccessible) &&
+      } else if ((roleMapEntry->accTypes & eTable) &&
                  frame->AccessibleType() != eHTMLTableType) {
         newAcc = new ARIAGridAccessibleWrap(content, document);
       }
     }
 
     if (!newAcc) {
       // Prefer to use markup (mostly tag name, perhaps attributes) to decide if
       // and what kind of accessible to create.
@@ -819,31 +819,29 @@ nsAccessibilityService::GetOrCreateAcces
       if (!newAcc)
         newAcc = CreateAccessibleByFrameType(frame, content, aContext);
 
       // If table has strong ARIA role then all table descendants shouldn't
       // expose their native roles.
       if (!roleMapEntry && newAcc) {
         if (frame->AccessibleType() == eHTMLTableRowType) {
           nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (contextRoleMap &&
-              !(contextRoleMap->accTypes & Accessible::eTableAccessible))
+          if (contextRoleMap && !(contextRoleMap->accTypes & eTable))
             roleMapEntry = &nsARIAMap::gEmptyRoleMap;
 
         } else if (frame->AccessibleType() == eHTMLTableCellType &&
                    aContext->ARIARoleMap() == &nsARIAMap::gEmptyRoleMap) {
           roleMapEntry = &nsARIAMap::gEmptyRoleMap;
 
         } else if (content->Tag() == nsGkAtoms::dt ||
                    content->Tag() == nsGkAtoms::li ||
                    content->Tag() == nsGkAtoms::dd ||
                    frame->AccessibleType() == eHTMLLiType) {
           nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (contextRoleMap &&
-              !(contextRoleMap->accTypes & Accessible::eListAccessible))
+          if (contextRoleMap && !(contextRoleMap->accTypes & eList))
             roleMapEntry = &nsARIAMap::gEmptyRoleMap;
         }
       }
     }
   }
 
   if (!newAcc) {
     // Elements may implement nsIAccessibleProvider via XBL. This allows them to
@@ -1171,17 +1169,17 @@ nsAccessibilityService::CreateAccessible
 }
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
                                                      nsIContent* aContent,
                                                      Accessible* aContext)
 {
   DocAccessible* document = aContext->Document();
-  if (aContext->IsOfType(Accessible::eTableRowAccessible)) {
+  if (aContext->IsTableRow()) {
     if (nsCoreUtils::IsHTMLTableHeader(aContent) &&
         aContext->GetContent() == aContent->GetParent()) {
       Accessible* accessible = new HTMLTableHeaderCellAccessibleWrap(aContent,
                                                                      document);
       NS_ADDREF(accessible);
       return accessible;
     }
 
@@ -1239,17 +1237,17 @@ nsAccessibilityService::CreateHTMLAccess
       return accessible;
     }
 
     Accessible* accessible = new HTMLLinkAccessible(aContent, document);
     NS_ADDREF(accessible);
     return accessible;
   }
 
-  if (aContext->IsOfType(Accessible::eListAccessible)) {
+  if (aContext->IsList()) {
     // If list item is a child of accessible list then create an accessible for
     // it unconditionally by tag name. nsBlockFrame creates the list item
     // accessible for other elements styled as list items.
     if (aContext->GetContent() == aContent->GetParent()) {
       if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) {
         Accessible* accessible = new HTMLLIAccessible(aContent, document);
         NS_ADDREF(accessible);
         return accessible;
@@ -1313,17 +1311,17 @@ nsAccessibilityService::CreateAccessible
       break;
     case eHTMLButtonType:
       newAcc = new HTMLButtonAccessible(aContent, document);
       break;
     case eHTMLCanvasType:
       newAcc = new HTMLCanvasAccessible(aContent, document);
       break;
     case eHTMLCaptionType:
-      if (aContext->IsOfType(Accessible::eTableAccessible) &&
+      if (aContext->IsTable() &&
           aContext->GetContent() == aContent->GetParent()) {
         newAcc = new HTMLCaptionAccessible(aContent, document);
       }
       break;
     case eHTMLCheckboxType:
       newAcc = new HTMLCheckboxAccessible(aContent, document);
       break;
     case eHTMLComboboxType:
@@ -1340,17 +1338,17 @@ nsAccessibilityService::CreateAccessible
       break;
     case eHTMLImageMapType:
       newAcc = new HTMLImageMapAccessible(aContent, document);
       break;
     case eHTMLLabelType:
       newAcc = new HTMLLabelAccessible(aContent, document);
       break;
     case eHTMLLiType:
-      if (aContext->IsOfType(Accessible::eListAccessible) &&
+      if (aContext->IsList() &&
           aContext->GetContent() == aContent->GetParent()) {
         newAcc = new HTMLLIAccessible(aContent, document);
       }
       break;
     case eHTMLSelectListType:
       newAcc = new HTMLSelectListAccessible(aContent, document);
       break;
     case eHTMLMediaType:
@@ -1359,24 +1357,24 @@ nsAccessibilityService::CreateAccessible
     case eHTMLRadioButtonType:
       newAcc = new HTMLRadioButtonAccessible(aContent, document);
       break;
     case eHTMLTableType:
       newAcc = new HTMLTableAccessibleWrap(aContent, document);
       break;
     case eHTMLTableCellType:
       // Accessible HTML table cell must be a child of accessible HTML table row.
-      if (aContext->IsOfType(Accessible::eHTMLTableRowAccessible))
+      if (aContext->IsHTMLTableRow())
         newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
       break;
 
     case eHTMLTableRowType: {
       // Accessible HTML table row must be a child of tbody/tfoot/thead of
       // accessible HTML table or must be a child of accessible of HTML table.
-      if (aContext->IsOfType(Accessible::eTableAccessible)) {
+      if (aContext->IsTable()) {
         nsIContent* parentContent = aContent->GetParent();
         nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
         if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
           parentContent = parentContent->GetParent();
           parentFrame = parentContent->GetPrimaryFrame();
         }
 
         if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
@@ -1395,17 +1393,17 @@ nsAccessibilityService::CreateAccessible
       break;
 
     case eImageType:
       newAcc = new ImageAccessibleWrap(aContent, document);
       break;
     case eOuterDocType:
       newAcc = new OuterDocAccessible(aContent, document);
       break;
-    case ePlugin: {
+    case ePluginType: {
       nsObjectFrame* objectFrame = do_QueryFrame(aFrame);
       newAcc = CreatePluginAccessible(objectFrame, aContent, aContext);
       break;
     }
     case eTextLeafType:
       newAcc = new TextLeafAccessibleWrap(aContent, document);
       break;
   }
--- a/accessible/src/generic/Accessible-inl.h
+++ b/accessible/src/generic/Accessible-inl.h
@@ -31,17 +31,17 @@ Accessible::ARIARole()
   return ARIATransformRole(mRoleMapEntry->role);
 }
 
 inline void
 Accessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
 {
   mRoleMapEntry = aRoleMapEntry;
   if (mRoleMapEntry)
-    mFlags |= mRoleMapEntry->accTypes;
+    mGenericTypes |= mRoleMapEntry->accTypes;
 }
 
 inline bool
 Accessible::HasNumericValue() const
 {
   if (mStateFlags & eHasNumericValue)
     return true;
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -68,16 +68,17 @@
 #include "nsAttrName.h"
 #include "nsNetUtil.h"
 #include "nsEventStates.h"
 
 #ifdef DEBUG
 #include "nsIDOMCharacterData.h"
 #endif
 
+#include "mozilla/Assertions.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 
@@ -141,17 +142,18 @@ Accessible::QueryInterface(REFNSIID aIID
   }
 
   return nsAccessNodeWrap::QueryInterface(aIID, aInstancePtr);
 }
 
 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
   nsAccessNodeWrap(aContent, aDoc),
   mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
-  mStateFlags(0), mFlags(0), mIndexOfEmbeddedChild(-1), mRoleMapEntry(nullptr)
+  mStateFlags(0), mType(0), mGenericTypes(0), mIndexOfEmbeddedChild(-1),
+  mRoleMapEntry(nullptr)
 {
 #ifdef NS_DEBUG_X
    {
      nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
      printf(">>> %p Created Acc - DOM: %p  PS: %p", 
             (void*)static_cast<nsIAccessible*>(this), (void*)aNode,
             (void*)shell.get());
     nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
@@ -2157,20 +2159,18 @@ Accessible::ScrollTo(uint32_t aHow)
 
 NS_IMETHODIMP
 Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
     return NS_ERROR_FAILURE;
 
-  nsIntPoint coords;
-  nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
-                                                  this, &coords);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
+                                                        this);
 
   nsIFrame *parentFrame = frame;
   while ((parentFrame = parentFrame->GetParent()))
     nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
 
   return NS_OK;
 }
 
@@ -3170,16 +3170,29 @@ Accessible::GetLevelInternal()
     } else {
       ++ level; // level is 1-index based
     }
   }
 
   return level;
 }
 
+void
+Accessible::StaticAsserts() const
+{
+  MOZ_STATIC_ASSERT(eLastChildrenFlag <= (2 << kChildrenFlagsBits) - 1,
+                    "Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
+  MOZ_STATIC_ASSERT(eLastStateFlag <= (2 << kStateFlagsBits) - 1,
+                    "Accessible::mStateFlags was oversized by eLastStateFlag!");
+  MOZ_STATIC_ASSERT(eLastAccType <= (2 << kTypeBits) - 1,
+                    "Accessible::mType was oversized by eLastAccType!");
+  MOZ_STATIC_ASSERT(eLastAccGenericType <= (2 << kGenericTypesBits) - 1,
+                    "Accessible::mGenericType was oversized by eLastAccGenericType!");
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // KeyBinding class
 
 void
 KeyBinding::ToPlatformFormat(nsAString& aValue) const
 {
   nsCOMPtr<nsIStringBundle> keyStringBundle;
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _Accessible_H_
 #define _Accessible_H_
 
+#include "mozilla/a11y/AccTypes.h"
 #include "mozilla/a11y/Role.h"
 #include "mozilla/a11y/States.h"
 #include "nsAccessNodeWrap.h"
 
 #include "nsIAccessible.h"
 #include "nsIAccessibleHyperLink.h"
 #include "nsIAccessibleSelectable.h"
 #include "nsIAccessibleValue.h"
@@ -460,63 +461,73 @@ public:
   // Downcasting and types
 
   inline bool IsAbbreviation() const
   {
     return mContent->IsHTML() &&
       (mContent->Tag() == nsGkAtoms::abbr || mContent->Tag() == nsGkAtoms::acronym);
   }
 
-  inline bool IsApplication() const { return mFlags & eApplicationAccessible; }
+  bool IsApplication() const { return mType == eApplicationType; }
 
-  bool IsAutoComplete() const { return mFlags & eAutoCompleteAccessible; }
+  bool IsAutoComplete() const { return mGenericTypes & eAutoComplete; }
 
-  inline bool IsAutoCompletePopup() const { return mFlags & eAutoCompletePopupAccessible; }
+  bool IsAutoCompletePopup() const
+    { return mGenericTypes & eAutoCompletePopup; }
 
-  inline bool IsCombobox() const { return mFlags & eComboboxAccessible; }
+  bool IsCombobox() const { return mGenericTypes & eCombobox; }
 
-  inline bool IsDoc() const { return mFlags & eDocAccessible; }
+  bool IsDoc() const { return mGenericTypes & eDocument; }
   DocAccessible* AsDoc();
 
-  inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; }
+  bool IsHyperText() const { return mGenericTypes & eHyperText; }
   HyperTextAccessible* AsHyperText();
 
-  inline bool IsHTMLFileInput() const { return mFlags & eHTMLFileInputAccessible; }
+  bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
+
+  bool IsHTMLListItem() const { return mType == eHTMLLiType; }
+  HTMLLIAccessible* AsHTMLListItem();
 
-  inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
-  mozilla::a11y::HTMLLIAccessible* AsHTMLListItem();
+  bool IsHTMLTableRow() const { return mType == eHTMLTableRowType; }
 
-  inline bool IsImage() const { return mFlags & eImageAccessible; }
-  mozilla::a11y::ImageAccessible* AsImage();
+  bool IsImage() const { return mType == eImageType; }
+  ImageAccessible* AsImage();
 
-  bool IsImageMapAccessible() const { return mFlags & eImageMapAccessible; }
-  mozilla::a11y::HTMLImageMapAccessible* AsImageMap();
+  bool IsImageMap() const { return mType == eImageMapType; }
+  HTMLImageMapAccessible* AsImageMap();
+
+  bool IsList() const { return mGenericTypes & eList; }
 
-  inline bool IsXULTree() const { return mFlags & eXULTreeAccessible; }
-  mozilla::a11y::XULTreeAccessible* AsXULTree();
+  bool IsListControl() const { return mGenericTypes & eListControl; }
 
-  inline bool IsXULDeck() const { return mFlags & eXULDeckAccessible; }
+  bool IsMenuButton() const { return mGenericTypes & eMenuButton; }
+
+  bool IsMenuPopup() const { return mType == eMenuPopupType; }
 
-  inline bool IsListControl() const { return mFlags & eListControlAccessible; }
+  bool IsProgress() const { return mType == eProgressType; }
 
-  inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; }
+  bool IsRoot() const { return mType == eRootType; }
+  a11y::RootAccessible* AsRoot();
 
-  inline bool IsMenuPopup() const { return mFlags & eMenuPopupAccessible; }
+  bool IsSelect() const { return mGenericTypes & eSelect; }
 
-  inline bool IsProgress() const { return mFlags & eProgressAccessible; }
+  bool IsTable() const { return mGenericTypes & eTable; }
+  virtual TableAccessible* AsTable() { return nullptr; }
 
-  inline bool IsRoot() const { return mFlags & eRootAccessible; }
-  mozilla::a11y::RootAccessible* AsRoot();
+  virtual TableCellAccessible* AsTableCell() { return nullptr; }
 
-  virtual mozilla::a11y::TableAccessible* AsTable() { return nullptr; }
+  bool IsTableRow() const { return mGenericTypes & eTableRow; }
 
-  virtual mozilla::a11y::TableCellAccessible* AsTableCell() { return nullptr; }
+  bool IsTextLeaf() const { return mType == eTextLeafType; }
+  TextLeafAccessible* AsTextLeaf();
 
-  inline bool IsTextLeaf() const { return mFlags & eTextLeafAccessible; }
-  mozilla::a11y::TextLeafAccessible* AsTextLeaf();
+  bool IsXULDeck() const { return mType == eXULDeckType; }
+
+  bool IsXULTree() const { return mType == eXULTreeType; }
+  XULTreeAccessible* AsXULTree();
 
   //////////////////////////////////////////////////////////////////////////////
   // ActionAccessible
 
   /**
    * Return the number of actions that can be performed on this accessible.
    */
   virtual uint8_t ActionCount();
@@ -583,22 +594,16 @@ public:
    * Returns an anchor URI at the given index.
    */
   virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex);
 
   //////////////////////////////////////////////////////////////////////////////
   // SelectAccessible
 
   /**
-   * Return true if the accessible is a select control containing selectable
-   * items.
-   */
-  bool IsSelect() const { return mFlags & eSelectAccessible; }
-
-  /**
    * Return an array of selected items.
    */
   virtual already_AddRefed<nsIArray> SelectedItems();
 
   /**
    * Return the number of selected items.
    */
   virtual uint32_t SelectedItemCount();
@@ -693,21 +698,16 @@ public:
 
   /**
    * Return true if the accessible has associated DOM content.
    */
   bool HasOwnContent() const
     { return mContent && !(mStateFlags & eSharedNode); }
 
   /**
-   * Return true if accessible is of given type.
-   */
-  bool IsOfType(uint32_t aType) const { return mFlags & aType; }
-
-  /**
   * Return true if the accessible has a numeric value.
   */
   bool HasNumericValue() const;
 
 protected:
 
   /**
    * Return the accessible name provided by native markup. It doesn't take
@@ -742,17 +742,19 @@ protected:
                                          nsresult *aError = nullptr) const;
 
   /**
    * Flags used to describe the state and type of children.
    */
   enum ChildrenFlags {
     eChildrenUninitialized = 0, // children aren't initialized
     eMixedChildren = 1 << 0, // text leaf children are presented
-    eEmbeddedChildren = 1 << 1 // all children are embedded objects
+    eEmbeddedChildren = 1 << 1, // all children are embedded objects
+
+    eLastChildrenFlag = eEmbeddedChildren
   };
 
   /**
    * Return true if the children flag is set.
    */
   bool IsChildrenFlag(ChildrenFlags aFlag) const
     { return static_cast<ChildrenFlags>(mChildrenFlags) == aFlag; }
 
@@ -765,49 +767,19 @@ protected:
    * Flags used to describe the state of this accessible.
    * @note keep these flags in sync with ChildrenFlags
    */
   enum StateFlags {
     eIsDefunct = 1 << 0, // accessible is defunct
     eIsNotInDocument = 1 << 1, // accessible is not in document
     eSharedNode = 1 << 2, // accessible shares DOM node from another accessible
     eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
-    eHasNumericValue = 1 << 4 // accessible has a numeric value
-  };
+    eHasNumericValue = 1 << 4, // accessible has a numeric value
 
-public: // XXX: a small hack to make these visible for nsARIAMap
-  /**
-   * Flags describing the type of this accessible.
-   * @note keep these flags in sync with ChildrenFlags and StateFlags
-   */
-  enum AccessibleTypes {
-    eApplicationAccessible = 1 << 0,
-    eAutoCompleteAccessible = 1 << 1,
-    eAutoCompletePopupAccessible = 1 << 2,
-    eComboboxAccessible = 1 << 3,
-    eDocAccessible = 1 << 4,
-    eHyperTextAccessible = 1 << 5,
-    eHTMLFileInputAccessible = 1 << 6,
-    eHTMLListItemAccessible = 1 << 7,
-    eHTMLTableRowAccessible = 1 << 8,
-    eImageAccessible = 1 << 9,
-    eImageMapAccessible = 1 << 10,
-    eListAccessible = 1 << 11,
-    eListControlAccessible = 1 << 12,
-    eMenuButtonAccessible = 1 << 13,
-    eMenuPopupAccessible = 1 << 14,
-    eProgressAccessible = 1 << 15,
-    eRootAccessible = 1 << 16,
-    eSelectAccessible = 1 << 17,
-    eTableAccessible = 1 << 18,
-    eTableCellAccessible = 1 << 19,
-    eTableRowAccessible = 1 << 20,
-    eTextLeafAccessible = 1 << 21,
-    eXULDeckAccessible = 1 << 22,
-    eXULTreeAccessible = 1 << 23
+    eLastStateFlag = eHasNumericValue
   };
 
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
   /**
@@ -904,22 +876,30 @@ protected:
    */
   virtual nsresult FirePlatformEvent(AccEvent* aEvent) = 0;
 
   // Data Members
   nsRefPtr<Accessible> mParent;
   nsTArray<nsRefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
+  static const uint8_t kChildrenFlagsBits = 2;
+  static const uint8_t kStateFlagsBits = 5;
+  static const uint8_t kTypeBits = 5;
+  static const uint8_t kGenericTypesBits = 12;
+
   /**
-   * Keep in sync with ChildrenFlags, StateFlags and AccessibleTypes.
+   * Keep in sync with ChildrenFlags, StateFlags and AccTypes.
    */
-  uint32_t mChildrenFlags : 2;
-  uint32_t mStateFlags : 5;
-  uint32_t mFlags : 25;
+  uint32_t mChildrenFlags : kChildrenFlagsBits;
+  uint32_t mStateFlags : kStateFlagsBits;
+  uint32_t mType : kTypeBits;
+  uint32_t mGenericTypes : kGenericTypesBits;
+
+  void StaticAsserts() const;
 
   friend class DocAccessible;
 
   nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
   int32_t mIndexOfEmbeddedChild;
   friend class EmbeddedObjCollector;
 
   nsAutoPtr<AccGroupInfo> mGroupInfo;
--- a/accessible/src/generic/ApplicationAccessible.cpp
+++ b/accessible/src/generic/ApplicationAccessible.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/Services.h"
 #include "nsIStringBundle.h"
 
 using namespace mozilla::a11y;
 
 ApplicationAccessible::ApplicationAccessible() :
   AccessibleWrap(nullptr, nullptr)
 {
-  mFlags |= eApplicationAccessible;
+  mType = eApplicationType;
   mAppInfo = do_GetService("@mozilla.org/xre/app-info;1");
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED1(ApplicationAccessible, Accessible,
                              nsIAccessibleApplication)
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -74,30 +74,30 @@ DocAccessible::
   DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
                   nsIPresShell* aPresShell) :
   HyperTextAccessibleWrap(aRootContent, this),
   mDocumentNode(aDocument), mScrollPositionChangedTicks(0),
   mLoadState(eTreeConstructionPending), mLoadEventType(0),
   mVirtualCursor(nullptr),
   mPresShell(aPresShell)
 {
-  mFlags |= eDocAccessible;
+  mGenericTypes |= eDocument;
   mStateFlags |= eNotNodeMapEntry;
 
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
 
   mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
   // If this is a XUL Document, it should not implement nsHyperText
   if (mDocumentNode && mDocumentNode->IsXUL())
-    mFlags &= ~eHyperTextAccessible;
+    mGenericTypes &= ~eHyperText;
 
   // For GTK+ native window, we do nothing here.
   if (!mDocumentNode)
     return;
 
   // DocManager creates document accessible when scrollable frame is
   // available already, it should be safe time to add scroll listener.
   AddScrollListener();
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -569,16 +569,15 @@ protected:
 private:
 
   nsIPresShell* mPresShell;
 };
 
 inline DocAccessible*
 Accessible::AsDoc()
 {
-  return mFlags & eDocAccessible ?
-    static_cast<DocAccessible*>(this) : nullptr;
+  return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/generic/FormControlAccessible.h
+++ b/accessible/src/generic/FormControlAccessible.h
@@ -17,17 +17,17 @@ namespace a11y {
 template<int Max>
 class ProgressMeterAccessible : public LeafAccessible
 {
 public:
   ProgressMeterAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     LeafAccessible(aContent, aDoc)
   {
     mStateFlags |= eHasNumericValue;
-    mFlags |= eProgressAccessible;
+    mType = eProgressType;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLEVALUE
 
   // Accessible
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -36,17 +36,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HyperTextAccessible::
   HyperTextAccessible(nsIContent* aNode, DocAccessible* aDoc) :
   AccessibleWrap(aNode, aDoc)
 {
-  mFlags |= eHyperTextAccessible;
+  mGenericTypes |= eHyperText;
 }
 
 NS_IMPL_ADDREF_INHERITED(HyperTextAccessible, AccessibleWrap)
 NS_IMPL_RELEASE_INHERITED(HyperTextAccessible, AccessibleWrap)
 
 nsresult
 HyperTextAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
@@ -1212,17 +1212,18 @@ HyperTextAccessible::GetRangeExtents(int
     return NS_ERROR_FAILURE;
   }
 
   *aX = boundsRect.x;
   *aY = boundsRect.y;
   *aWidth = boundsRect.width;
   *aHeight = boundsRect.height;
 
-  return nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
+  nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
+  return NS_OK;
 }
 
 /*
  * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to
  * the screen or this widget's window depending on coords.
  */
 NS_IMETHODIMP
 HyperTextAccessible::GetOffsetAtPoint(int32_t aX, int32_t aY,
@@ -1233,20 +1234,18 @@ HyperTextAccessible::GetOffsetAtPoint(in
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsIFrame *hyperFrame = GetFrame();
   if (!hyperFrame) {
     return NS_ERROR_FAILURE;
   }
 
-  nsIntPoint coords;
-  nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
-                                                  this, &coords);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
+                                                        this);
 
   nsPresContext* presContext = mDoc->PresContext();
   nsPoint coordsInAppUnits =
     coords.ToAppUnits(presContext->AppUnitsPerDevPixel());
 
   nsRect frameScreenRect = hyperFrame->GetScreenRectInAppUnits();
   if (!frameScreenRect.Contains(coordsInAppUnits.x, coordsInAppUnits.y))
     return NS_OK;   // Not found, will return -1
@@ -1895,23 +1894,21 @@ HyperTextAccessible::ScrollSubstringToPo
                                             int32_t aEndIndex,
                                             uint32_t aCoordinateType,
                                             int32_t aX, int32_t aY)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
     return NS_ERROR_FAILURE;
 
-  nsIntPoint coords;
-  nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
-                                                  this, &coords);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
+                                                        this);
 
   nsRefPtr<nsRange> range = new nsRange();
-  rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex, range);
+  nsresult rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex, range);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsPresContext* presContext = frame->PresContext();
   nsPoint coordsInAppUnits =
     coords.ToAppUnits(presContext->AppUnitsPerDevPixel());
 
   bool initialScrolled = false;
   nsIFrame *parentFrame = frame;
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -395,17 +395,16 @@ private:
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
 inline HyperTextAccessible*
 Accessible::AsHyperText()
 {
-  return mFlags & eHyperTextAccessible ?
-    static_cast<HyperTextAccessible*>(this) : nullptr;
+  return IsHyperText() ? static_cast<HyperTextAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/generic/ImageAccessible.cpp
+++ b/accessible/src/generic/ImageAccessible.cpp
@@ -26,17 +26,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // ImageAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 ImageAccessible::
   ImageAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   LinkableAccessible(aContent, aDoc)
 {
-  mFlags |= eImageAccessible;
+  mType = eImageType;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED1(ImageAccessible, Accessible,
                              nsIAccessibleImage)
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public
 
@@ -153,17 +153,18 @@ ImageAccessible::DoAction(uint8_t aIndex
 NS_IMETHODIMP
 ImageAccessible::GetImagePosition(uint32_t aCoordType, int32_t* aX, int32_t* aY)
 {
   int32_t width, height;
   nsresult rv = GetBounds(aX, aY, &width, &height);
   if (NS_FAILED(rv))
     return rv;
 
-  return nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
+  nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 ImageAccessible::GetImageSize(int32_t* aWidth, int32_t* aHeight)
 {
   int32_t x, y;
   return GetBounds(&x, &y, aWidth, aHeight);
 }
--- a/accessible/src/generic/RootAccessible.cpp
+++ b/accessible/src/generic/RootAccessible.cpp
@@ -56,17 +56,17 @@ NS_IMPL_ISUPPORTS_INHERITED1(RootAccessi
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/destructor
 
 RootAccessible::
   RootAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
                  nsIPresShell* aPresShell) :
   DocAccessibleWrap(aDocument, aRootContent, aPresShell)
 {
-  mFlags |= eRootAccessible;
+  mType = eRootType;
 }
 
 RootAccessible::~RootAccessible()
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible
--- a/accessible/src/generic/RootAccessible.h
+++ b/accessible/src/generic/RootAccessible.h
@@ -80,16 +80,15 @@ protected:
 #endif
 
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
 inline RootAccessible*
 Accessible::AsRoot()
 {
-  return mFlags & eRootAccessible ?
-    static_cast<mozilla::a11y::RootAccessible*>(this) : nullptr;
+  return IsRoot() ? static_cast<mozilla::a11y::RootAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/generic/TextLeafAccessible.cpp
+++ b/accessible/src/generic/TextLeafAccessible.cpp
@@ -14,17 +14,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // TextLeafAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 TextLeafAccessible::
   TextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   LinkableAccessible(aContent, aDoc)
 {
-  mFlags |= eTextLeafAccessible;
+  mType = eTextLeafType;
 }
 
 TextLeafAccessible::~TextLeafAccessible()
 {
 }
 
 role
 TextLeafAccessible::NativeRole()
--- a/accessible/src/generic/TextLeafAccessible.h
+++ b/accessible/src/generic/TextLeafAccessible.h
@@ -40,17 +40,16 @@ protected:
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcast method
 
 inline TextLeafAccessible*
 Accessible::AsTextLeaf()
 {
-  return mFlags & eTextLeafAccessible ?
-    static_cast<TextLeafAccessible*>(this) : nullptr;
+  return IsTextLeaf() ? static_cast<TextLeafAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -527,17 +527,17 @@ HTMLTextFieldAccessible::ContainerWidget
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLFileInputAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLFileInputAccessible::
 HTMLFileInputAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   HyperTextAccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eHTMLFileInputAccessible;
+  mType = eHTMLFileInputType;
 }
 
 role
 HTMLFileInputAccessible::NativeRole()
 {
   // JAWS wants a text container, others don't mind. No specific role in
   // AT APIs.
   return roles::TEXT_CONTAINER;
--- a/accessible/src/html/HTMLImageMapAccessible.cpp
+++ b/accessible/src/html/HTMLImageMapAccessible.cpp
@@ -23,17 +23,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLImageMapAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLImageMapAccessible::
   HTMLImageMapAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   ImageAccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eImageMapAccessible;
+  mType = eImageMapType;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLImageMapAccessible: nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLImageMapAccessible, ImageAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/html/HTMLImageMapAccessible.h
+++ b/accessible/src/html/HTMLImageMapAccessible.h
@@ -71,16 +71,15 @@ protected:
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
 inline HTMLImageMapAccessible*
 Accessible::AsImageMap()
 {
-  return IsImageMapAccessible() ?
-    static_cast<HTMLImageMapAccessible*>(this) : nullptr;
+  return IsImageMap() ? static_cast<HTMLImageMapAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/html/HTMLListAccessible.cpp
+++ b/accessible/src/html/HTMLListAccessible.cpp
@@ -41,17 +41,17 @@ HTMLListAccessible::NativeState()
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLLIAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLLIAccessible::
   HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   HyperTextAccessibleWrap(aContent, aDoc), mBullet(nullptr)
 {
-  mFlags |= eHTMLListItemAccessible;
+  mType = eHTMLLiType;
 
   nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
   if (blockFrame && blockFrame->HasBullet()) {
     mBullet = new HTMLListBulletAccessible(mContent, mDoc);
     if (!Document()->BindToDocument(mBullet, nullptr))
       mBullet = nullptr;
   }
 }
--- a/accessible/src/html/HTMLListAccessible.h
+++ b/accessible/src/html/HTMLListAccessible.h
@@ -17,17 +17,17 @@ class HTMLListBulletAccessible;
 
 /**
  * Used for HTML list (like HTML ul).
  */
 class HTMLListAccessible : public HyperTextAccessibleWrap
 {
 public:
   HTMLListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-    HyperTextAccessibleWrap(aContent, aDoc) { mFlags |= eListAccessible; }
+    HyperTextAccessibleWrap(aContent, aDoc) { mGenericTypes |= eList; }
   virtual ~HTMLListAccessible() { }
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // Accessible
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
@@ -95,16 +95,15 @@ public:
    */
   bool IsInside() const;
 };
 
 
 inline HTMLLIAccessible*
 Accessible::AsHTMLListItem()
 {
-  return mFlags & eHTMLListItemAccessible ?
-    static_cast<HTMLLIAccessible*>(this) : nullptr;
+  return IsHTMLListItem() ? static_cast<HTMLLIAccessible*>(this) : nullptr;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/html/HTMLSelectAccessible.cpp
+++ b/accessible/src/html/HTMLSelectAccessible.cpp
@@ -26,17 +26,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLSelectListAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLSelectListAccessible::
   HTMLSelectListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eSelectAccessible | eListControlAccessible;
+  mGenericTypes |= eListControl | eSelect;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLSelectListAccessible: Accessible public
 
 uint64_t
 HTMLSelectListAccessible::NativeState()
 {
@@ -392,17 +392,17 @@ HTMLSelectOptGroupAccessible::CacheChild
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLComboboxAccessible::
   HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eComboboxAccessible;
+  mGenericTypes |= eCombobox;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible: Accessible
 
 role
 HTMLComboboxAccessible::NativeRole()
 {
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -342,17 +342,17 @@ HTMLTableRowAccessible::NativeRole()
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLTableAccessible::
   HTMLTableAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc), xpcAccessibleTable(this)
 {
-  mFlags |= eTableAccessible;
+  mGenericTypes |= eTable;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableAccessible: nsISupports implementation
 
 NS_IMPL_ISUPPORTS_INHERITED1(HTMLTableAccessible, Accessible,
                              nsIAccessibleTable)
 
--- a/accessible/src/html/HTMLTableAccessible.h
+++ b/accessible/src/html/HTMLTableAccessible.h
@@ -88,17 +88,20 @@ public:
 /**
  * HTML table row accessible (html:tr).
  */
 class HTMLTableRowAccessible : public AccessibleWrap
 {
 public:
   HTMLTableRowAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     AccessibleWrap(aContent, aDoc)
-    { mFlags |= eTableRowAccessible | eHTMLTableRowAccessible; }
+  {
+    mType = eHTMLTableRowType;
+    mGenericTypes |= eTableRow;
+  }
   virtual ~HTMLTableRowAccessible() { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // Accessible
   virtual a11y::role NativeRole();
 };
 
--- a/accessible/src/xul/XULColorPickerAccessible.cpp
+++ b/accessible/src/xul/XULColorPickerAccessible.cpp
@@ -83,17 +83,17 @@ XULColorPickerTileAccessible::ContainerW
 ////////////////////////////////////////////////////////////////////////////////
 // XULColorPickerAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULColorPickerAccessible::
   XULColorPickerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULColorPickerTileAccessible(aContent, aDoc)
 {
-  mFlags |= eMenuButtonAccessible;
+  mGenericTypes |= eMenuButton;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULColorPickerAccessible: Accessible
 
 uint64_t
 XULColorPickerAccessible::NativeState()
 {
--- a/accessible/src/xul/XULComboboxAccessible.cpp
+++ b/accessible/src/xul/XULComboboxAccessible.cpp
@@ -23,19 +23,19 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 
 XULComboboxAccessible::
   XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                             nsGkAtoms::autocomplete, eIgnoreCase))
-    mFlags |= eAutoCompleteAccessible;
+    mGenericTypes |= eAutoComplete;
   else
-    mFlags |= eComboboxAccessible;
+    mGenericTypes |= eCombobox;
 }
 
 role
 XULComboboxAccessible::NativeRole()
 {
   return IsAutoComplete() ? roles::AUTOCOMPLETE : roles::COMBOBOX;
 }
 
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -35,17 +35,17 @@ using namespace mozilla::a11y;
 // XULButtonAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULButtonAccessible::
   XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   if (ContainsMenu())
-    mFlags |= eMenuButtonAccessible;
+    mGenericTypes |= eMenuButton;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULButtonAccessible: nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED0(XULButtonAccessible, Accessible)
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/xul/XULListboxAccessible.cpp
+++ b/accessible/src/xul/XULListboxAccessible.cpp
@@ -103,17 +103,17 @@ XULListboxAccessible::
   XULListboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULSelectControlAccessible(aContent, aDoc), xpcAccessibleTable(this)
 {
   nsIContent* parentContent = mContent->GetParent();
   if (parentContent) {
     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
       do_QueryInterface(parentContent);
     if (autoCompletePopupElm)
-      mFlags |= eAutoCompletePopupAccessible;
+      mGenericTypes |= eAutoCompletePopup;
   }
 }
 
 NS_IMPL_ADDREF_INHERITED(XULListboxAccessible, XULSelectControlAccessible)
 NS_IMPL_RELEASE_INHERITED(XULListboxAccessible, XULSelectControlAccessible)
 
 nsresult
 XULListboxAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
--- a/accessible/src/xul/XULMenuAccessible.cpp
+++ b/accessible/src/xul/XULMenuAccessible.cpp
@@ -431,22 +431,22 @@ XULMenuSeparatorAccessible::ActionCount(
 ////////////////////////////////////////////////////////////////////////////////
 
 XULMenupopupAccessible::
   XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULSelectControlAccessible(aContent, aDoc)
 {
   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   if (menuPopupFrame && menuPopupFrame->IsMenu())
-    mFlags |= eMenuPopupAccessible;
+    mType = eMenuPopupType;
 
   // May be the anonymous <menupopup> inside <menulist> (a combobox)
   mSelectControl = do_QueryInterface(mContent->GetParent());
   if (!mSelectControl)
-    mFlags &= ~eSelectAccessible;
+    mGenericTypes &= ~eSelect;
 }
 
 uint64_t
 XULMenupopupAccessible::NativeState()
 {
   uint64_t state = Accessible::NativeState();
 
 #ifdef DEBUG
--- a/accessible/src/xul/XULSelectControlAccessible.cpp
+++ b/accessible/src/xul/XULSelectControlAccessible.cpp
@@ -26,17 +26,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULSelectControlAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULSelectControlAccessible::
   XULSelectControlAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eSelectAccessible;
+  mGenericTypes |= eSelect;
   mSelectControl = do_QueryInterface(aContent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULSelectControlAccessible: nsAccessNode
 
 void
 XULSelectControlAccessible::Shutdown()
--- a/accessible/src/xul/XULTabAccessible.h
+++ b/accessible/src/xul/XULTabAccessible.h
@@ -62,17 +62,17 @@ protected:
 /**
  * A container of tab panels, xul:tabpanels element.
  */
 class XULDeckAccessible : public AccessibleWrap
 {
 public:
   XULDeckAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     AccessibleWrap(aContent, aDoc)
-    { mFlags |= eXULDeckAccessible; }
+    { mType = eXULDeckType; }
 
   // Accessible
   virtual a11y::role NativeRole();
 };
 
 
 /**
  * A tabpanel object, child elements of xul:tabpanels element. Note,the object
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -32,33 +32,34 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeAccessible::
   XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
-  mFlags |= eSelectAccessible | eXULTreeAccessible;
+  mType = eXULTreeType;
+  mGenericTypes |= eSelect;
 
   mTree = nsCoreUtils::GetTreeBoxObject(aContent);
   NS_ASSERTION(mTree, "Can't get mTree!\n");
 
   if (mTree) {
     nsCOMPtr<nsITreeView> treeView;
     mTree->GetView(getter_AddRefs(treeView));
     mTreeView = treeView;
   }
 
   nsIContent* parentContent = mContent->GetParent();
   if (parentContent) {
     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
       do_QueryInterface(parentContent);
     if (autoCompletePopupElm)
-      mFlags |= eAutoCompletePopupAccessible;
+      mGenericTypes |= eAutoCompletePopup;
   }
 
   mAccessibleCache.Init(kDefaultTreeCacheSize);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible: nsISupports and cycle collection implementation
 
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -23,17 +23,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeGridAccessible::
   XULTreeGridAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULTreeAccessible(aContent, aDoc), xpcAccessibleTable(this)
 {
-  mFlags |= eTableAccessible;
+  mGenericTypes |= eTable;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridAccessible: nsISupports implementation
 
 NS_IMPL_ISUPPORTS_INHERITED1(XULTreeGridAccessible,
                              XULTreeAccessible,
                              nsIAccessibleTable)
@@ -275,17 +275,17 @@ XULTreeGridAccessible::CreateTreeItemAcc
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeGridRowAccessible::
   XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc,
                            Accessible* aTreeAcc, nsITreeBoxObject* aTree,
                            nsITreeView* aTreeView, int32_t aRow) :
   XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView, aRow)
 {
-  mFlags |= eTableRowAccessible;
+  mGenericTypes |= eTableRow;
 
   mAccessibleCache.Init(kDefaultTreeCacheSize);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridRowAccessible: nsISupports and cycle collection implementation
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(XULTreeGridRowAccessible,
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -549,16 +549,18 @@ pref("dom.ipc.processPriorityManager.ena
 pref("dom.ipc.processPriorityManager.gracePeriodMS", 1000);
 
 // Kernel parameters for how processes are killed on low-memory.
 pref("gonk.systemMemoryPressureRecoveryPollMS", 5000);
 pref("hal.processPriorityManager.gonk.masterOomScoreAdjust", 0);
 pref("hal.processPriorityManager.gonk.masterKillUnderMB", 1);
 pref("hal.processPriorityManager.gonk.foregroundOomScoreAdjust", 67);
 pref("hal.processPriorityManager.gonk.foregroundKillUnderMB", 4);
+pref("hal.processPriorityManager.gonk.backgroundHomescreenOomScoreAdjust", 200);
+pref("hal.processPriorityManager.gonk.backgroundHomescreenKillUnderMB", 5);
 pref("hal.processPriorityManager.gonk.backgroundOomScoreAdjust", 400);
 pref("hal.processPriorityManager.gonk.backgroundKillUnderMB", 8);
 pref("hal.processPriorityManager.gonk.notifyLowMemUnderMB", 10);
 
 // Niceness values (i.e., CPU priorities) for B2G processes.
 pref("hal.processPriorityManager.gonk.masterNice", -1);
 pref("hal.processPriorityManager.gonk.foregroundNice", 0);
 pref("hal.processPriorityManager.gonk.backgroundNice", 10);
--- a/b2g/chrome/content/dbg-browser-actors.js
+++ b/b2g/chrome/content/dbg-browser-actors.js
@@ -98,69 +98,31 @@ DeviceRootActor.prototype.requestTypes =
  *        The browser instance that contains this tab.
  */
 function DeviceTabActor(connection, browser) {
   BrowserTabActor.call(this, connection, browser);
 }
 
 DeviceTabActor.prototype = new BrowserTabActor();
 
-DeviceTabActor.prototype.grip = function DTA_grip() {
-  dbg_assert(!this.exited,
-             'grip() should not be called on exited browser actor.');
-  dbg_assert(this.actorID,
-             'tab should have an actorID.');
-
-  let response = {
-    'actor': this.actorID,
-    'title': this.browser.title,
-    'url': this.browser.document.documentURI
-  };
-
-  // Walk over tab actors added by extensions and add them to a new ActorPool.
-  let actorPool = new ActorPool(this.conn);
-  this._createExtraActors(DebuggerServer.tabActorFactories, actorPool);
-  if (!actorPool.isEmpty()) {
-    this._tabActorPool = actorPool;
-    this.conn.addActorPool(this._tabActorPool);
-  }
-
-  this._appendExtraActors(response);
-  return response;
-};
-
-/**
- * Creates a thread actor and a pool for context-lifetime actors. It then sets
- * up the content window for debugging.
- */
-DeviceTabActor.prototype._pushContext = function DTA_pushContext() {
-  dbg_assert(!this._contextPool, "Can't push multiple contexts");
+Object.defineProperty(DeviceTabActor.prototype, "title", {
+  get: function() {
+    return this.browser.title;
+  },
+  enumerable: true,
+  configurable: false
+});
 
-  this._contextPool = new ActorPool(this.conn);
-  this.conn.addActorPool(this._contextPool);
-
-  this.threadActor = new ThreadActor(this, this.browser.wrappedJSObject);
-  this._contextPool.addActor(this.threadActor);
-};
-
-// Protocol Request Handlers
+Object.defineProperty(DeviceTabActor.prototype, "url", {
+  get: function() {
+    return this.browser.document.documentURI;
+  },
+  enumerable: true,
+  configurable: false
+});
 
-/**
- * Prepare to enter a nested event loop by disabling debuggee events.
- */
-DeviceTabActor.prototype.preNest = function DTA_preNest() {
-  let windowUtils = this.browser
-                        .QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDOMWindowUtils);
-  windowUtils.suppressEventHandling(true);
-  windowUtils.suspendTimeouts();
-};
-
-/**
- * Prepare to exit a nested event loop by enabling debuggee events.
- */
-DeviceTabActor.prototype.postNest = function DTA_postNest(aNestData) {
-  let windowUtils = this.browser
-                        .QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDOMWindowUtils);
-  windowUtils.resumeTimeouts();
-  windowUtils.suppressEventHandling(false);
-};
+Object.defineProperty(DeviceTabActor.prototype, "contentWindow", {
+  get: function() {
+    return this.browser;
+  },
+  enumerable: true,
+  configurable: false
+});
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -20,16 +20,17 @@ const Cc = Components.classes;
 
 const PROMPT_FOR_UNKNOWN = ['geolocation'];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "PermSettings",
                                    "@mozilla.org/permissionSettings;1",
                                    "nsIDOMPermissionSettings");
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r169730"
+"clang_version": "r170377"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56115091,
-"digest": "d7188264f28d6f6a84fab9737520cad22fe3d575917a87fc110d0b9a2033617c35da83530ea20553f5c55a996459f750fa2d0c49288c1fb9671f6252a92cf4c9",
+"size": 56131193,
+"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -15,17 +15,17 @@ include $(DEPTH)/config/autoconf.mk
                  browser_social.js \
                  browser_social_toolbar.js \
                  browser_social_shareButton.js \
                  browser_social_sidebar.js \
                  browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
-                 $(filter disabled-temporarily--bug-820489, browser_social_multiprovider.js) \
+                 browser_social_multiprovider.js \
                  social_panel.html \
                  social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -12,16 +12,33 @@ function test() {
     iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
   };
   runSocialTestWithProvider(manifest, function (finishcb) {
     runSocialTests(tests, undefined, undefined, finishcb);
   });
 }
 
 var tests = {
+  testProfileNone: function(next, useNull) {
+    let profile = useNull ? null : {};
+    Social.provider.updateUserProfile(profile);
+    // check dom values
+    let portrait = document.getElementsByClassName("social-statusarea-user-portrait")[0].getAttribute("src");
+    // this is the default image for the profile area when not logged in.
+    is(portrait, "chrome://global/skin/icons/information-32.png", "portrait is empty");
+    let userDetailsBroadcaster = document.getElementById("socialBroadcaster_userDetails");
+    let notLoggedInStatusValue = userDetailsBroadcaster.getAttribute("notLoggedInLabel");
+    let userButton = document.getElementsByClassName("social-statusarea-loggedInStatus")[0];
+    ok(!userButton.hidden, "username is visible");
+    is(userButton.getAttribute("label"), notLoggedInStatusValue, "label reflects not being logged in");
+    next();
+  },
+  testProfileNull: function(next) {
+    this.testProfileNone(next, true);
+  },
   testProfileSet: function(next) {
     let profile = {
       portrait: "https://example.com/portrait.jpg",
       userName: "trickster",
       displayName: "Kuma Lisa",
       profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
     }
     Social.provider.updateUserProfile(profile);
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -1248,17 +1248,17 @@ DownloadsViewItemController.prototype = 
 
     downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
     {
       this.dataItem.cancel();
     },
 
     downloadsCmd_open: function DVIC_downloadsCmd_open()
     {
-      this.dataItem.openLocalFile();
+      this.dataItem.openLocalFile(window);
       // We explicitly close the panel here to give the user the feedback that
       // their click has been received, and we're handling the action.
       // Otherwise, we'd have to wait for the file-type handler to execute
       // before the panel would close. This also helps to prevent the user from
       // accidentally opening a file several times.
       DownloadsPanel.hidePanel();
     },
 
--- a/browser/components/places/content/downloadsView.js
+++ b/browser/components/places/content/downloadsView.js
@@ -113,16 +113,22 @@ DownloadElementShell.prototype = {
   get downloadURI() {
     if (this._dataItem)
      return this._dataItem.uri;
     if (this._placesNode)
       return this._placesNode.uri;
     throw new Error("Unexpected download element state");
   },
 
+  get _downloadURIObj() {
+    if (!("__downloadURIObj" in this))
+      this.__downloadURIObj = NetUtil.newURI(this.downloadURI);
+    return this.__downloadURIObj;
+  },
+
   get _icon() {
     if (this._targetFileURI)
       return "moz-icon://" + this._targetFileURI + "?size=32";
     if (this._placesNode)
       return this.placesNode.icon;
     if (this._dataItem)
       throw new Error("Session-download items should always have a target file uri");
     throw new Error("Unexpected download element state");
@@ -131,17 +137,17 @@ DownloadElementShell.prototype = {
   // Helper for getting a places annotation set for the download.
   _getAnnotation: function DES__getAnnotation(aAnnotation, aDefaultValue) {
     if (this._annotations.has(aAnnotation))
       return this._annotations.get(aAnnotation);
 
     let value;
     try {
       value = PlacesUtils.annotations.getPageAnnotation(
-        NetUtil.newURI(this.downloadURI), aAnnotation);
+        this._downloadURIObj, aAnnotation);
     }
     catch(ex) {
       if (aDefaultValue === undefined) {
         throw new Error("Could not get required annotation '" + aAnnotation +
                         "' for download with url '" + this.downloadURI + "'");
       }
       value = aDefaultValue;
     }
@@ -158,17 +164,17 @@ DownloadElementShell.prototype = {
   },
 
   // The label for the download
   get _displayName() {
     if (this._dataItem)
       return this._dataItem.target;
 
     try {
-      return this._getAnnotation(DESTINATION_FILE_NAME_ANNO, "");
+      return this._getAnnotation(DESTINATION_FILE_NAME_ANNO);
     }
     catch(ex) { }
 
     // Fallback to the places title, or, at last, to the download uri.
     return this._placesNode.title || this.downloadURI;
   },
 
   // If there's a target file for the download, this is its nsIFile object.
@@ -461,17 +467,17 @@ DownloadElementShell.prototype = {
       case "downloadsCmd_cancel": {
         this._dataItem.cancel();
         break;
       }
       case "cmd_delete": {
         if (this._dataItem)
           this._dataItem.remove();
         if (this._placesNode)
-          PlacesUtils.bhistory.removePage(NetUtil.newURI(this.downloadURI));
+          PlacesUtils.bhistory.removePage(this._downloadURIObj);
         break;
        }
       case "downloadsCmd_retry": {
         if (this._dataItem)
           this._dataItem.retry();
         else
           this._retryAsHistoryDownload();
         break;
@@ -572,28 +578,45 @@ DownloadsPlacesView.prototype = {
     if (this._downloadElementsShellsForURI.has(aURI)) {
       let downloadElementShells = this._downloadElementsShellsForURI.get(aURI);
       for (let des of downloadElementShells) {
         aCallback(des);
       }
     }
   },
 
-  // Given a data item for a session download, or a places node for a past
-  // download, updates the view as necessary.
-  // 1. If the given data is a places node, we check whether there are any
-  //    element for the same download url. If there are, then we just reset
-  //    their places node. Otherwise we add a new download element.
-  // 2. If the given data is a data item, we first check if there's an history
-  //    download in the list that is not associated with a data item. If we found
-  //    one, we use it for the data item as well and reposition it alongside the
-  //    other session downloads. If we don't, then we go ahead and create a new
-  //    element for the download.
+  /**
+   * Given a data item for a session download, or a places node for a past
+   * download, updates the view as necessary.
+   *  1. If the given data is a places node, we check whether there are any
+   *     elements for the same download url. If there are, then we just reset
+   *     their places node. Otherwise we add a new download element.
+   *  2. If the given data is a data item, we first check if there's a history
+   *     download in the list that is not associated with a data item. If we
+   *     found one, we use it for the data item as well and reposition it
+   *     alongside the other session downloads. If we don't, then we go ahead
+   *     and create a new element for the download.
+   *
+   * @param aDataItem
+   *        The data item of a session download. Set to null for history
+   *        downloads data.
+   * @param [optional] aPlacesNode
+   *        The places node for a history download. Required if there's no data
+   *        item.
+   * @param [optional] aNewest
+   *        @see onDataItemAdded. Ignored for history downlods.
+   * @param [optional] aDocumentFragment
+   *        To speed up the appending of multiple elements to the end of the
+   *        list which are coming in a single batch (i.e. invalidateContainer),
+   *        a document fragment may be passed to which the new elements would
+   *        be appended. It's the caller's job to ensure the fragment is merged
+   *        to the richlistbox at the end.
+   */
   _addDownloadData:
-  function DPV_addDownload(aDataItem, aPlacesNode, aNewest) {
+  function DPV_addDownload(aDataItem, aPlacesNode, aNewest = false, aDocumentFragment = null) {
     let downloadURI = aPlacesNode ? aPlacesNode.uri : aDataItem.uri;
     let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI, null);
     if (!shellsForURI) {
       shellsForURI = new Set();
       this._downloadElementsShellsForURI.set(downloadURI, shellsForURI);
     }
 
     let newOrUpdatedShell = null;
@@ -653,22 +676,23 @@ DownloadsPlacesView.prototype = {
         this._richlistbox.insertBefore(newOrUpdatedShell.element,
                                        this._richlistbox.firstChild);
         if (!this._lastSessionDownloadElement) {
           this._lastSessionDownloadElement = newOrUpdatedShell.element;
         }
       }
       else if (aDataItem) {
         let before = this._lastSessionDownloadElement ?
-          this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild
-        this._richlistbox.insertBefore(newOrUpdatedShell.element, before)
+          this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild;
+        this._richlistbox.insertBefore(newOrUpdatedShell.element, before);
         this._lastSessionDownloadElement = newOrUpdatedShell.element;
       }
       else {
-        this._richlistbox.appendChild(newOrUpdatedShell.element);
+        let appendTo = aDocumentFragment || this._richlistbox;
+        appendTo.appendChild(newOrUpdatedShell.element);
       }
 
       if (this.searchTerm) {
         newOrUpdatedShell.element.hidden =
           !newOrUpdatedShell.element._shell.matchesSearchTerm(this.searchTerm);
       }
     }
   },
@@ -816,28 +840,32 @@ DownloadsPlacesView.prototype = {
 
     // Remove the invalidated history downloads from the list and unset the
     // places node for data downloads.
     for (let element of this._richlistbox.childNodes) {
       if (element._shell.placesNode)
         this._removeHistoryDownloadFromView(element._shell.placesNode);
     }
 
+    let elementsToAppendFragment = document.createDocumentFragment();
     for (let i = 0; i < aContainer.childCount; i++) {
       try {
-        this._addDownloadData(null, aContainer.getChild(i), false)
+        this._addDownloadData(null, aContainer.getChild(i), false,
+                              elementsToAppendFragment);
       }
       catch(ex) {
         Cu.reportError(ex);
       }
     }
+
+    this._richlistbox.appendChild(elementsToAppendFragment);
   },
 
   nodeInserted: function DPV_nodeInserted(aParent, aPlacesNode) {
-    this._addDownloadData(null, aPlacesNode, false);
+    this._addDownloadData(null, aPlacesNode);
   },
 
   nodeRemoved: function DPV_nodeRemoved(aParent, aPlacesNode, aOldIndex) {
     this._removeHistoryDownloadFromView(aPlacesNode);
   },
 
   nodeIconChanged: function DPV_nodeIconChanged(aNode) {
     this._forEachDownloadElementShellForURI(aNode.uri, function(aDownloadElementShell) {
@@ -890,17 +918,17 @@ DownloadsPlacesView.prototype = {
   onDataLoadStarting: function() { },
   onDataLoadCompleted: function() { },
 
   onDataItemAdded: function DPV_onDataItemAdded(aDataItem, aNewest) {
     this._addDownloadData(aDataItem, null, aNewest);
   },
 
   onDataItemRemoved: function DPV_onDataItemRemoved(aDataItem) {
-    this._removeSessionDownloadFromView(aDataItem)
+    this._removeSessionDownloadFromView(aDataItem);
   },
 
   getViewItem: function(aDataItem)
     this._viewItemsForDataItems.get(aDataItem, null),
 
   supportsCommand: function(aCommand)
     DOWNLOAD_VIEW_SUPPORTED_COMMANDS.indexOf(aCommand) != -1,
 
@@ -934,17 +962,17 @@ DownloadsPlacesView.prototype = {
                 createInstance(Ci.nsITransferable);
     trans.init(null);
 
     let flavors = ["text/x-moz-url", "text/unicode"];
     flavors.forEach(trans.addDataFlavor);
 
     Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
 
-    // Getting the data or creating the nsIURI might fail
+    // Getting the data or creating the nsIURI might fail.
     try {
       let data = {};
       trans.getAnyTransferData({}, data, {});
       let [url, name] = data.value.QueryInterface(Ci.nsISupportsString)
                             .data.split("\n");
       if (url)
         return [NetUtil.newURI(url, null, null).spec, name];
     }
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -1268,18 +1268,18 @@ let SessionStoreInternal = {
     delete browser.__SS_data;
     delete browser.__SS_tabStillLoading;
     delete browser.__SS_formDataSaved;
     delete browser.__SS_hostSchemeData;
 
     // If this tab was in the middle of restoring or still needs to be restored,
     // we need to reset that state. If the tab was restoring, we will attempt to
     // restore the next tab.
-    let previousState;
-    if (previousState = browser.__SS_restoreState) {
+    let previousState = browser.__SS_restoreState;
+    if (previousState) {
       this._resetTabRestoringState(aTab);
       if (previousState == TAB_STATE_RESTORING)
         this.restoreNextTab();
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -137,16 +137,17 @@ ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 MOCHITEST_BROWSER_FILES += \
 	browser_354894_perwindowpb.js \
 	browser_394759_perwindowpb.js \
 	$(NULL)
 else
 MOCHITEST_BROWSER_FILES += \
 	browser_248970_a.js \
 	browser_248970_b.js \
++	browser_248970_b_perwindowpb.js \
 	browser_354894.js \
 	browser_394759_privatebrowsing.js \
 	$(NULL)
 endif
 
 # Disabled on Windows for frequent intermittent failures
 ifneq ($(OS_ARCH), WINNT)
 MOCHITEST_FILES += \
copy from browser/components/sessionstore/test/browser_248970_b.js
copy to browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
--- a/browser/components/sessionstore/test/browser_248970_b.js
+++ b/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
@@ -1,27 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   /** Test (B) for Bug 248970 **/
+  waitForExplicitFinish();
 
-  function test(aLambda) {
-    try {
-      return aLambda() || true;
-    } catch(ex) { }
-    return false;
-  }
-
-  var file = Components.classes["@mozilla.org/file/directory_service;1"]
-             .getService(Components.interfaces.nsIProperties)
-             .get("TmpD", Components.interfaces.nsIFile);
-  var filePath = file.path;
-
+  let windowsToClose = [];
+  let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  let filePath = file.path;
   let fieldList = {
     "//input[@name='input']":     Date.now().toString(),
     "//input[@name='spaced 1']":  Math.random().toString(),
     "//input[3]":                 "three",
     "//input[@type='checkbox']":  true,
     "//input[@name='uncheck']":   false,
     "//input[@type='radio'][1]":  false,
     "//input[@type='radio'][2]":  true,
@@ -29,16 +21,29 @@ function test() {
     "//select":                   2,
     "//select[@multiple]":        [1, 3],
     "//textarea[1]":              "",
     "//textarea[2]":              "Some text... " + Math.random(),
     "//textarea[3]":              "Some more text\n" + new Date(),
     "//input[@type='file']":      filePath
   };
 
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(win) {
+      win.close();
+    });
+  });
+
+  function test(aLambda) {
+    try {
+      return aLambda() || true;
+    } catch(ex) { }
+    return false;
+  }
+
   function getElementByXPath(aTab, aQuery) {
     let doc = aTab.linkedBrowser.contentDocument;
     let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
     return doc.evaluate(aQuery, doc, null, xptype, null).singleNodeValue;
   }
 
   function setFormValue(aTab, aQuery, aValue) {
     let node = getElementByXPath(aTab, aQuery);
@@ -63,113 +68,100 @@ function test() {
     if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
       return aValue == node.value;
     if (!node.multiple)
       return aValue == node.selectedIndex;
     return Array.every(node.options, function(aOpt, aIx)
             (aValue.indexOf(aIx) > -1) == aOpt.selected);
   }
 
-  // test setup
-  waitForExplicitFinish();
-
-  // private browsing service
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-  gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-
   //////////////////////////////////////////////////////////////////
-  // Test (B) : Session data restoration between modes            //
+  // Test (B) : Session data restoration between windows          //
   //////////////////////////////////////////////////////////////////
 
   let rootDir = getRootDirectory(gTestPath);
   const testURL = rootDir + "browser_248970_b_sample.html";
   const testURL2 = "http://mochi.test:8888/browser/" +
-  "browser/components/sessionstore/test/browser_248970_b_sample.html";
+    "browser/components/sessionstore/test/browser_248970_b_sample.html";
 
-  // get closed tab count
-  let count = ss.getClosedTabCount(window);
-  let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
-  ok(0 <= count && count <= max_tabs_undo,
-    "getClosedTabCount should return zero or at most max_tabs_undo");
+  whenNewWindowLoaded(false, function(aWin) {
+    windowsToClose.push(aWin);
 
-  // setup a state for tab (A) so we can check later that is restored
-  let key = "key";
-  let value = "Value " + Math.random();
-  let state = { entries: [{ url: testURL }], extData: { key: value } };
+    // get closed tab count
+    let count = ss.getClosedTabCount(aWin);
+    let max_tabs_undo =
+      Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
+    ok(0 <= count && count <= max_tabs_undo,
+      "getClosedTabCount should return zero or at most max_tabs_undo");
 
-  // public session, add new tab: (A)
-  let tab_A = gBrowser.addTab(testURL);
-  ss.setTabState(tab_A, JSON.stringify(state));
-  tab_A.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
-
-    // make sure that the next closed tab will increase getClosedTabCount
-    gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1)
+    // setup a state for tab (A) so we can check later that is restored
+    let key = "key";
+    let value = "Value " + Math.random();
+    let state = { entries: [{ url: testURL }], extData: { key: value } };
 
-    // populate tab_A with form data
-    for (let i in fieldList)
-      setFormValue(tab_A, i, fieldList[i]);
-
-    // public session, close tab: (A)
-    gBrowser.removeTab(tab_A);
-
-    // verify that closedTabCount increased
-    ok(ss.getClosedTabCount(window) > count, "getClosedTabCount has increased after closing a tab");
+    // public session, add new tab: (A)
+    let tab_A = aWin.gBrowser.addTab(testURL);
+    ss.setTabState(tab_A, JSON.stringify(state));
+    whenBrowserLoaded(tab_A.linkedBrowser, function() {
+      // make sure that the next closed tab will increase getClosedTabCount
+      Services.prefs.setIntPref(
+        "browser.sessionstore.max_tabs_undo", max_tabs_undo + 1)
 
-    // verify tab: (A), in undo list
-    let tab_A_restored = test(function() ss.undoCloseTab(window, 0));
-    ok(tab_A_restored, "a tab is in undo list");
-    tab_A_restored.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
+      // populate tab_A with form data
+      for (let i in fieldList)
+        setFormValue(tab_A, i, fieldList[i]);
 
-      is(testURL, this.currentURI.spec, "it's the same tab that we expect");
-      gBrowser.removeTab(tab_A_restored);
+      // public session, close tab: (A)
+      aWin.gBrowser.removeTab(tab_A);
 
-      // enter private browsing mode
-      pb.privateBrowsingEnabled = true;
-      ok(pb.privateBrowsingEnabled, "private browsing enabled");
+      // verify that closedTabCount increased
+      ok(ss.getClosedTabCount(aWin) > count,
+         "getClosedTabCount has increased after closing a tab");
 
-      // setup a state for tab (B) so we can check that its duplicated properly
-      let key1 = "key1";
-      let value1 = "Value " + Math.random();
-      let state1 = { entries: [{ url: testURL2 }], extData: { key1: value1 } };
+      // verify tab: (A), in undo list
+      let tab_A_restored = test(function() ss.undoCloseTab(aWin, 0));
+      ok(tab_A_restored, "a tab is in undo list");
+      whenBrowserLoaded(tab_A_restored.linkedBrowser, function() {
+        is(testURL, tab_A_restored.linkedBrowser.currentURI.spec,
+           "it's the same tab that we expect");
+        aWin.gBrowser.removeTab(tab_A_restored);
 
-      // private browsing session, new tab: (B)
-      let tab_B = gBrowser.addTab(testURL2);
-      ss.setTabState(tab_B, JSON.stringify(state1));
-      tab_B.linkedBrowser.addEventListener("load", function(aEvent) {
-        this.removeEventListener("load", arguments.callee, true);
+        whenNewWindowLoaded(true, function(aWin) {
+          windowsToClose.push(aWin);
 
-        // populate tab: (B) with different form data
-        for (let item in fieldList)
-          setFormValue(tab_B, item, fieldList[item]);
-
-        // duplicate tab: (B)
-        let tab_C = gBrowser.duplicateTab(tab_B);
-        tab_C.linkedBrowser.addEventListener("load", function(aEvent) {
-          this.removeEventListener("load", arguments.callee, true);
+          // setup a state for tab (B) so we can check that its duplicated
+          // properly
+          let key1 = "key1";
+          let value1 = "Value " + Math.random();
+          let state1 = {
+            entries: [{ url: testURL2 }], extData: { key1: value1 }
+          };
 
-          // verify the correctness of the duplicated tab
-          is(ss.getTabValue(tab_C, key1), value1,
-            "tab successfully duplicated - correct state");
+          let tab_B = aWin.gBrowser.addTab(testURL2);
+          ss.setTabState(tab_B, JSON.stringify(state1));
+          whenBrowserLoaded(tab_B.linkedBrowser, function() {
+            // populate tab: (B) with different form data
+            for (let item in fieldList)
+              setFormValue(tab_B, item, fieldList[item]);
 
-          for (let item in fieldList)
-            ok(compareFormValue(tab_C, item, fieldList[item]),
-              "The value for \"" + item + "\" was correctly duplicated");
-
-          // private browsing session, close tab: (C) and (B)
-          gBrowser.removeTab(tab_C);
-          gBrowser.removeTab(tab_B);
+            // duplicate tab: (B)
+            let tab_C = aWin.gBrowser.duplicateTab(tab_B);
+            whenBrowserLoaded(tab_C.linkedBrowser, function() {
+              // verify the correctness of the duplicated tab
+              is(ss.getTabValue(tab_C, key1), value1,
+                "tab successfully duplicated - correct state");
 
-          // exit private browsing mode
-          pb.privateBrowsingEnabled = false;
-          ok(!pb.privateBrowsingEnabled, "private browsing disabled");
+              for (let item in fieldList)
+                ok(compareFormValue(tab_C, item, fieldList[item]),
+                  "The value for \"" + item + "\" was correctly duplicated");
+
+              // private browsing session, close tab: (C) and (B)
+              aWin.gBrowser.removeTab(tab_C);
+              aWin.gBrowser.removeTab(tab_B);
 
-          // cleanup
-          if (gPrefService.prefHasUserValue("browser.privatebrowsing.keep_current_session"))
-            gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
-          finish();
-        }, true);
-      }, true);
-    }, true);
-  }, true);
+              finish();
+            });
+          });
+        });
+      });
+    });
+  });
 }
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -272,8 +272,16 @@ registerCleanupFunction(function () {
 // because apparently it's buggy. See bug 599253.
 function closeAllButPrimaryWindow() {
   for (let win in BrowserWindowIterator()) {
     if (win != window) {
       win.close();
     }
   }
 }
+
+function whenNewWindowLoaded(aIsPrivate, aCallback) {
+  let win = OpenBrowserWindow({private: aIsPrivate});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    aCallback(win);
+  }, false);
+}
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -59,25 +59,27 @@ let UI = {
   _cleanupFunctions: [],
   
   // Constant: _maxInteractiveWait
   // If the UI is in the middle of an operation, this is the max amount of
   // milliseconds to wait between input events before we no longer consider
   // the operation interactive.
   _maxInteractiveWait: 250,
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
   // Variable: _privateBrowsing
   // Keeps track of info related to private browsing, including: 
   //   transitionMode - whether we're entering or exiting PB
   //   wasInTabView - whether TabView was visible before we went into PB
   _privateBrowsing: {
     transitionMode: "",
     wasInTabView: false 
   },
-  
+#endif
+
   // Variable: _storageBusy
   // Tells whether the storage is currently busy or not.
   _storageBusy: false,
 
   // Variable: isDOMWindowClosing
   // Tells wether the parent window is about to close
   isDOMWindowClosing: false,
 
@@ -590,18 +592,23 @@ let UI = {
       mainWindow.removeAttribute("activetitlebarcolor");
       mainWindow.removeAttribute("inactivetitlebarcolor");
     }
   },
 #endif
 
   // ----------
   // Function: storageBusy
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  // Pauses the storage activity that conflicts with sessionstore updates.
+  // Calls can be nested.
+#else
   // Pauses the storage activity that conflicts with sessionstore updates and 
   // private browsing mode switches. Calls can be nested. 
+#endif
   storageBusy: function UI_storageBusy() {
     if (this._storageBusy)
       return;
 
     this._storageBusy = true;
 
     TabItems.pauseReconnecting();
     GroupItems.pauseAutoclose();
@@ -644,16 +651,17 @@ let UI = {
     gWindow.addEventListener("SSWindowStateBusy", handleSSWindowStateBusy, false);
     gWindow.addEventListener("SSWindowStateReady", handleSSWindowStateReady, false);
 
     this._cleanupFunctions.push(function() {
       gWindow.removeEventListener("SSWindowStateBusy", handleSSWindowStateBusy, false);
       gWindow.removeEventListener("SSWindowStateReady", handleSSWindowStateReady, false);
     });
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     // Private Browsing:
     // When transitioning to PB, we exit Panorama if necessary (making note of the
     // fact that we were there so we can return after PB) and make sure we
     // don't reenter Panorama due to all of the session restore tab
     // manipulation (which otherwise we might). When transitioning away from
     // PB, we reenter Panorama if we had been there directly before PB.
     function pbObserver(subject, topic, data) {
       if (topic == "private-browsing") {
@@ -684,16 +692,17 @@ let UI = {
     Services.obs.addObserver(pbObserver, "private-browsing-change-granted", false);
     Services.obs.addObserver(pbObserver, "private-browsing-transition-complete", false);
 
     this._cleanupFunctions.push(function() {
       Services.obs.removeObserver(pbObserver, "private-browsing");
       Services.obs.removeObserver(pbObserver, "private-browsing-change-granted");
       Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
     });
+#endif
 
     // TabOpen
     this._eventListeners.open = function (event) {
       let tab = event.target;
 
       // if it's an app tab, add it to all the group items
       if (tab.pinned)
         GroupItems.addAppTab(tab);
@@ -709,17 +718,21 @@ let UI = {
       if (tab.pinned)
         GroupItems.removeAppTab(tab);
         
       if (self.isTabViewVisible()) {
         // just closed the selected tab in the TabView interface.
         if (self._currentTab == tab)
           self._closedSelectedTabInTabView = true;
       } else {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        // If we're currently in the process of session store update,
+#else
         // If we're currently in the process of entering private browsing,
+#endif
         // we don't want to go to the Tab View UI. 
         if (self._storageBusy)
           return;
 
         // if not closing the last tab
         if (gBrowser.tabs.length > 1) {
           // Don't return to TabView if there are any app tabs
           for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
@@ -820,19 +833,24 @@ let UI = {
   // Function: onTabSelect
   // Called when the user switches from one tab to another outside of the TabView UI.
   onTabSelect: function UI_onTabSelect(tab) {
     this._currentTab = tab;
 
     if (this.isTabViewVisible()) {
       // We want to zoom in if:
       // 1) we didn't just restore a tab via Ctrl+Shift+T
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      // 2) the currently selected tab is the last created tab and has a tabItem
+      if (!this.restoredClosedTab &&
+#else
       // 2) we're not in the middle of switching from/to private browsing
       // 3) the currently selected tab is the last created tab and has a tabItem
       if (!this.restoredClosedTab && !this._privateBrowsing.transitionMode &&
+#endif
           this._lastOpenedTab == tab && tab._tabViewTabItem) {
         tab._tabViewTabItem.zoomIn(true);
         this._lastOpenedTab = null;
         return;
       }
       if (this._closedLastVisibleTab ||
           (this._closedSelectedTabInTabView && !this.closedLastTabInTabView) ||
           this.restoredClosedTab) {
@@ -1007,18 +1025,20 @@ let UI = {
     // to handle both upper and lower cases here.
     [
 #ifdef XP_UNIX
       "redo",
 #endif
 #ifdef XP_MACOSX
       "fullScreen",
 #endif
-      "closeWindow", "tabview", "undoCloseTab", "undoCloseWindow",
-      "privatebrowsing"
+      "closeWindow", "tabview", "undoCloseTab", "undoCloseWindow"
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      , "privatebrowsing"
+#endif
     ].forEach(function(key) {
       let element = gWindow.document.getElementById("key_" + key);
       let code = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
       keys[code] = key;
     });
     this._browserKeysWithShift = keys;
   },
 
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r169730"
+"clang_version": "r170377"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 61875575,
-"digest": "7098e1cc85a8ae45672333320c7673c284023366fbe6d4fab3e9276960b7f92fd74ee63d0ad6c43a86fd96a1b886408993479260ac897fa6fe101c4a9fb807b1",
+"size": 61878564,
+"digest": "bc344ad6cb8f4d7b25447a8f06ae3a22c6b90283fcc70f28f12578bdaf01ec960a774cdc215bdda4960cef04b6a991e462daeeda4f7e802bf65e6c9a3967f66c",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r169730"
+"clang_version": "r170377"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 62279267,
-"digest": "0c9f87e3d7675feaa79b6d5d0997c8f370785901b8668f51da3339ae674291b3946e9d76629adb1c4ccd15aa851d84e4cd39ddd9032c916cc75233fe73c68f2e",
+"size": 62277867,
+"digest": "b9c6ab4069e336fcbe705f07fd2beda37aecfd4078863898826c9591305b92f3f3f762e7f5c1b0eeb7e0fb1c0dacbca60f24868e0b3181bd34dcd9c5d44d20ee",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/macosx32/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx32/releng.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r169730"
+"clang_version": "r170377"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56115091,
-"digest": "d7188264f28d6f6a84fab9737520cad22fe3d575917a87fc110d0b9a2033617c35da83530ea20553f5c55a996459f750fa2d0c49288c1fb9671f6252a92cf4c9",
+"size": 56131193,
+"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r169730"
+"clang_version": "r170377"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56115091,
-"digest": "d7188264f28d6f6a84fab9737520cad22fe3d575917a87fc110d0b9a2033617c35da83530ea20553f5c55a996459f750fa2d0c49288c1fb9671f6252a92cf4c9",
+"size": 56131193,
+"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -18,59 +18,41 @@ if test "$OS_ARCH" = "WINNT"; then
             "$MOZ_UPDATE_CHANNEL" = "release"; then
       if ! test "$MOZ_DEBUG"; then
         MOZ_STUB_INSTALLER=1
       fi
     fi
   fi
 fi
 
-# The value of ACCEPTED_MAR_CHANNEL_IDS should usually be the same as the value
-# MAR_CHANNEL_ID. If more than one ID is needed, then you should use a comma
-# separated list of values.
-# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
-case "$MOZ_UPDATE_CHANNEL" in
-release|aurora|esr)
-  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-$MOZ_UPDATE_CHANNEL
-  MAR_CHANNEL_ID=firefox-mozilla-$MOZ_UPDATE_CHANNEL
-  ;;
-beta)
-  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-beta,firefox-mozilla-release
-  MAR_CHANNEL_ID=firefox-mozilla-beta
-  ;;
-*)
-  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
-  MAR_CHANNEL_ID=firefox-mozilla-central
-  ;;
-esac
-
-# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
-# Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
-# because branding dependencies are broken.
-# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
-# specified. It should never point to the "official" branding directory.
-# For mozilla-beta, mozilla-release, or mozilla-central repositories, use
-# "nightly" branding (until bug 659568 is fixed).
-# For the mozilla-aurora repository, use "aurora".
-MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
-if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then
-  MOZ_BRANDING_DIRECTORY=browser/branding/aurora
-else
-  MOZ_BRANDING_DIRECTORY=browser/branding/nightly
-fi
-
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_AITC=1
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_CRYPTO=1
 MOZ_SERVICES_METRICS=1
 MOZ_SERVICES_NOTIFICATIONS=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gio"
+# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
+# Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
+# because branding dependencies are broken.
+# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
+# specified. It should never point to the "official" branding directory.
+# For mozilla-beta, mozilla-release, or mozilla-central repositories, use
+# "nightly" branding (until bug 659568 is fixed).
+# For the mozilla-aurora repository, use "aurora".
+MOZ_BRANDING_DIRECTORY=browser/branding/nightly
+MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
 MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+# This should usually be the same as the value MAR_CHANNEL_ID.
+# If more than one ID is needed, then you should use a comma separated list
+# of values.
+ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBAPP_RUNTIME=1
 MOZ_MEDIA_NAVIGATOR=1
 MOZ_PER_WINDOW_PRIVATE_BROWSING=1
--- a/browser/devtools/debugger/DebuggerPanel.jsm
+++ b/browser/devtools/debugger/DebuggerPanel.jsm
@@ -20,17 +20,17 @@ function DebuggerPanel(iframeWindow, too
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
   this._controller = this.panelWin.DebuggerController;
   this._view = this.panelWin.DebuggerView;
   this._controller._target = this.target;
   this._bkp = this._controller.Breakpoints;
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 DebuggerPanel.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   open: function DebuggerPanel_open() {
     let deferred = Promise.defer();
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -210,19 +210,28 @@ let DebuggerController = {
 
     this.client = null;
     this.tabClient = null;
     this.activeThread = null;
   },
 
   /**
    * Called for each location change in the debugged tab.
+   *
+   * @param string aType
+   *        Packet type.
+   * @param object aPacket
+   *        Packet received from the server.
    */
-  _onTabNavigated: function DC__onTabNavigated() {
-    DebuggerView._handleTabNavigation();
+  _onTabNavigated: function DC__onTabNavigated(aType, aPacket) {
+    if (aPacket.state == "start") {
+      DebuggerView._handleTabNavigation();
+      return;
+    }
+
     this.ThreadState._handleTabNavigation();
     this.StackFrames._handleTabNavigation();
     this.SourceScripts._handleTabNavigation();
   },
 
   /**
    * Called when the debugged tab is closed.
    */
--- a/browser/devtools/debugger/test/browser_dbg_bfcache.js
+++ b/browser/devtools/debugger/test/browser_dbg_bfcache.js
@@ -35,17 +35,24 @@ function testInitialLoad() {
   });
 
   gDebuggee.firstCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
-    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+    gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+      dump("tabNavigated state " + aPacket.state + "\n");
+      if (aPacket.state == "start") {
+        return;
+      }
+
+      gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
       ok(true, "tabNavigated event was fired.");
       info("Still attached to the tab.");
 
       gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
         gDebugger.removeEventListener(aEvent.type, _onEvent);
 
         executeSoon(function() {
           validateSecondPage();
@@ -54,17 +61,24 @@ function testLocationChange()
       });
     });
     content.location = STACK_URL;
   });
 }
 
 function testBack()
 {
-  gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+  gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+    dump("tabNavigated state " + aPacket.state + "\n");
+    if (aPacket.state == "start") {
+      return;
+    }
+
+    gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
     ok(true, "tabNavigated event was fired after going back.");
     info("Still attached to the tab.");
 
     gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
       gDebugger.removeEventListener(aEvent.type, _onEvent);
 
       executeSoon(function() {
         validateFirstPage();
@@ -74,17 +88,24 @@ function testBack()
   });
 
   info("Going back.");
   content.history.back();
 }
 
 function testForward()
 {
-  gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+  gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+    dump("tabNavigated state " + aPacket.state + "\n");
+    if (aPacket.state == "start") {
+      return;
+    }
+
+    gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
     ok(true, "tabNavigated event was fired after going forward.");
     info("Still attached to the tab.");
 
     gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
       gDebugger.removeEventListener(aEvent.type, _onEvent);
 
       executeSoon(function() {
         validateSecondPage();
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
@@ -68,17 +68,23 @@ function testSimpleCall() {
     "The source editor text should not be 'Loading...'");
 
   testLocationChange();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
-    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+    gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+      dump("tabNavigated state " + aPacket.state + "\n");
+      if (aPacket.state == "start") {
+        return;
+      }
+      gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
       ok(true, "tabNavigated event was fired.");
       info("Still attached to the tab.");
 
       gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
         gDebugger.removeEventListener(aEvent.type, _onEvent);
 
         is(gDebugger.DebuggerView.Sources.selectedValue, null,
           "There should be no selected script.");
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
@@ -68,17 +68,23 @@ function testSimpleCall() {
     "The source editor text should not be 'Loading...'");
 
   testLocationChange();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
-    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+    gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+      dump("tabNavigated state " + aPacket.state + "\n");
+      if (aPacket.state == "start") {
+        return;
+      }
+      gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
       ok(true, "tabNavigated event was fired.");
       info("Still attached to the tab.");
 
       gDebugger.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
         gDebugger.removeEventListener(aEvent.type, _onEvent);
 
         isnot(gDebugger.DebuggerView.Sources.selectedValue, null,
           "There should be a selected script.");
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -45,17 +45,23 @@ function testSimpleCall() {
   });
 
   gDebuggee.simpleCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
-    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+    gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+      dump("tabNavigated state " + aPacket.state + "\n");
+      if (aPacket.state == "start") {
+        return;
+      }
+      gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
+
       ok(true, "tabNavigated event was fired.");
       info("Still attached to the tab.");
 
       closeDebuggerAndFinish();
     });
     content.location = TAB1_URL;
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_nav-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_nav-01.js
@@ -21,17 +21,23 @@ function test()
 }
 
 function get_tab()
 {
   gTab1 = addTab(TAB1_URL, function() {
     get_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
       gTab1Actor = aGrip.actor;
       gClient.request({ to: aGrip.actor, type: "attach" }, function(aResponse) {
-        gClient.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+        gClient.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
+          dump("onTabNavigated state " + aPacket.state + "\n");
+          if (aPacket.state == "start") {
+            return;
+          }
+          gClient.removeListener("tabNavigated", onTabNavigated);
+
           is(aPacket.url, TAB2_URL, "Got a tab navigation notification.");
           gClient.addOneTimeListener("tabDetached", function (aEvent, aPacket) {
             ok(true, "Got a tab detach notification.");
             finish_test();
           });
           removeTab(gTab1);
         });
         gTab1.linkedBrowser.loadURI(TAB2_URL);
--- a/browser/devtools/framework/Sidebar.jsm
+++ b/browser/devtools/framework/Sidebar.jsm
@@ -21,17 +21,17 @@ const XULNS = "http://www.mozilla.org/ke
  *  <tabbox> node;
  * @param {ToolPanel} panel
  *  Related ToolPanel instance;
  * @param {Boolean} showTabstripe
  *  Show the tabs.
  */
 this.ToolSidebar = function ToolSidebar(tabbox, panel, showTabstripe=true)
 {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   this._tabbox = tabbox;
   this._panelDoc = this._tabbox.ownerDocument;
   this._toolPanel = panel;
 
   this._tabbox.tabpanels.addEventListener("select", this, true);
 
   this._tabs = new Map();
--- a/browser/devtools/framework/Target.jsm
+++ b/browser/devtools/framework/Target.jsm
@@ -165,17 +165,17 @@ Object.defineProperty(Target.prototype, 
 });
 
 
 /**
  * A TabTarget represents a page living in a browser tab. Generally these will
  * be web pages served over http(s), but they don't have to be.
  */
 function TabTarget(tab) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
   this._tab = tab;
   this._setupListeners();
 }
 
 TabTarget.prototype = {
   _webProgressListener: null,
 
   supports: supports,
@@ -196,16 +196,20 @@ TabTarget.prototype = {
   get url() {
     return this._tab.linkedBrowser.contentDocument.location.href;
   },
 
   get isRemote() {
     return false;
   },
 
+  get isLocalTab() {
+    return true;
+  },
+
   /**
    * Listen to the different tabs events.
    */
   _setupListeners: function TabTarget__setupListeners() {
     this._webProgressListener = new TabWebProgressListener(this);
     this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
     this.tab.addEventListener("TabClose", this);
     this.tab.parentNode.addEventListener("TabSelect", this);
@@ -300,17 +304,17 @@ TabWebProgressListener.prototype = {
 };
 
 
 /**
  * A WindowTarget represents a page living in a xul window or panel. Generally
  * these will have a chrome: URL
  */
 function WindowTarget(window) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
   this._window = window;
 }
 
 WindowTarget.prototype = {
   supports: supports,
   get version() { return getVersion(); },
 
   get window() {
@@ -324,16 +328,20 @@ WindowTarget.prototype = {
   get url() {
     return this._window.document.location.href;
   },
 
   get isRemote() {
     return false;
   },
 
+  get isLocalTab() {
+    return false;
+  },
+
   /**
    * Target is not alive anymore.
    */
   destroy: function() {
     if (!this._destroyed) {
       this._destroyed = true;
 
       this.emit("close");
@@ -349,46 +357,52 @@ WindowTarget.prototype = {
     return 'WindowTarget:' + this.window;
   },
 };
 
 /**
  * A RemoteTarget represents a page living in a remote Firefox instance.
  */
 function RemoteTarget(form, client, chrome) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
   this._client = client;
   this._form = form;
   this._chrome = chrome;
 
   this.destroy = this.destroy.bind(this);
   this.client.addListener("tabDetached", this.destroy);
 
-  this._onTabNavigated = function onRemoteTabNavigated() {
-    this.emit("navigate");
+  this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
+    if (aPacket.state == "start") {
+      this.emit("will-navigate", aPacket);
+    } else {
+      this.emit("navigate", aPacket);
+    }
   }.bind(this);
   this.client.addListener("tabNavigated", this._onTabNavigated);
 }
 
 RemoteTarget.prototype = {
   supports: supports,
   get version() getVersion(),
 
   get isRemote() true,
 
   get chrome() this._chrome,
 
-  get name() this._form._title,
+  get name() this._form.title,
 
-  get url() this._form._url,
+  get url() this._form.url,
 
   get client() this._client,
 
   get form() this._form,
 
+  get isLocalTab() false,
+
   /**
    * Target is not alive anymore.
    */
   destroy: function RT_destroy() {
     // If several things call destroy then we give them all the same
     // destruction promise so we're sure to destroy only once
     if (this._destroyer) {
       return this._destroyer.promise;
--- a/browser/devtools/framework/Toolbox.jsm
+++ b/browser/devtools/framework/Toolbox.jsm
@@ -122,17 +122,17 @@ this.Toolbox = function Toolbox(target, 
   let definitions = gDevTools.getToolDefinitions();
   if (!definitions.get(selectedTool)) {
     selectedTool = "webconsole";
   }
   this._defaultToolId = selectedTool;
 
   this._host = this._createHost(hostType);
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   gDevTools.on("tool-registered", this._toolRegistered);
   gDevTools.on("tool-unregistered", this._toolUnregistered);
 }
 
 /**
  * The toolbox can be 'hosted' either embedded in a browser window
  * or in a separate window.
@@ -265,16 +265,20 @@ Toolbox.prototype = {
    */
   _buildDockButtons: function TBOX_createDockButtons() {
     let dockBox = this.doc.getElementById("toolbox-dock-buttons");
 
     while (dockBox.firstChild) {
       dockBox.removeChild(dockBox.firstChild);
     }
 
+    if (!this._target.isLocalTab) {
+      return;
+    }
+
     let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
 
     for each (let position in this.HostType) {
       if (position == this.hostType ||
          (!sideEnabled && position == this.HostType.SIDE)) {
         continue;
       }
 
@@ -471,16 +475,20 @@ Toolbox.prototype = {
    * @param {string} hostType
    *        The host type of the new host object
    */
   switchHost: function TBOX_switchHost(hostType) {
     if (hostType == this._host.type) {
       return;
     }
 
+    if (!this._target.isLocalTab) {
+      return;
+    }
+
     let newHost = this._createHost(hostType);
     return newHost.open().then(function(iframe) {
       // change toolbox document's parent to the new host
       iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
       iframe.swapFrameLoaders(this.frame);
 
       this._host.off("window-closed", this.destroy);
       this._host.destroy();
--- a/browser/devtools/framework/ToolboxHosts.jsm
+++ b/browser/devtools/framework/ToolboxHosts.jsm
@@ -28,17 +28,17 @@ this.Hosts = {
 }
 
 /**
  * Host object for the dock on the bottom of the browser
  */
 function BottomHost(hostTab) {
   this.hostTab = hostTab;
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 BottomHost.prototype = {
   type: "bottom",
 
   heightPref: "devtools.toolbox.footer.height",
 
   /**
@@ -96,17 +96,17 @@ BottomHost.prototype = {
 
 
 /**
  * Host object for the in-browser sidebar
  */
 function SidebarHost(hostTab) {
   this.hostTab = hostTab;
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 SidebarHost.prototype = {
   type: "side",
 
   widthPref: "devtools.toolbox.sidebar.width",
 
   /**
@@ -161,17 +161,17 @@ SidebarHost.prototype = {
 }
 
 /**
  * Host object for the toolbox in a separate window
  */
 function WindowHost() {
   this._boundUnload = this._boundUnload.bind(this);
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 WindowHost.prototype = {
   type: "window",
 
   WINDOW_URL: "chrome://browser/content/devtools/framework/toolbox-window.xul",
 
   /**
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -26,17 +26,17 @@ const FORBIDDEN_IDS = new Set("toolbox",
  */
 this.DevTools = function DevTools() {
   this._tools = new Map();
   this._toolboxes = new Map();
 
   // destroy() is an observer's handler so we need to preserve context.
   this.destroy = this.destroy.bind(this);
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   Services.obs.addObserver(this.destroy, "quit-application", false);
 
   // Register the set of default tools
   for (let definition of defaultTools) {
     this.registerTool(definition);
   }
 }
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -80,17 +80,17 @@ function finishUp() {
 * When a Toolbox is started it creates a DevToolPanel for each of the tools
 * by calling toolDefinition.build(). The returned object should
 * at least implement these functions. They will be used by the ToolBox.
 *
 * There may be no benefit in doing this as an abstract type, but if nothing
 * else gives us a place to write documentation.
 */
 function DevToolPanel(iframeWindow, toolbox) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
 
   /*let doc = iframeWindow.document
   let label = doc.createElement("label");
   let textNode = doc.createTextNode("Some Tool");
 
   label.appendChild(textNode);
--- a/browser/devtools/framework/toolbox.xul
+++ b/browser/devtools/framework/toolbox.xul
@@ -8,20 +8,28 @@
 <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/toolbox.css" type="text/css"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/source-editor-overlay.xul"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <notificationbox id="toolbox-notificationbox" flex="1">
     <toolbar class="devtools-tabbar">
+#ifdef XP_MACOSX
       <hbox id="toolbox-controls">
         <toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
         <hbox id="toolbox-dock-buttons"/>
       </hbox>
+#endif
       <radiogroup id="toolbox-tabs" orient="horizontal">
       </radiogroup>
       <hbox id="toolbox-buttons" flex="1" pack="end"/>
+#ifndef XP_MACOSX
+      <hbox id="toolbox-controls">
+        <hbox id="toolbox-dock-buttons"/>
+        <toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
+      </hbox>
+#endif
     </toolbar>
     <deck id="toolbox-deck" flex="1">
     </deck>
   </notificationbox>
 </window>
--- a/browser/devtools/inspector/Highlighter.jsm
+++ b/browser/devtools/inspector/Highlighter.jsm
@@ -79,17 +79,17 @@ this.Highlighter = function Highlighter(
   this.target = aTarget;
   this.tab = aTarget.tab;
   this.toolbox = aToolbox;
   this.browser = this.tab.linkedBrowser;
   this.chromeDoc = this.tab.ownerDocument;
   this.chromeWin = this.chromeDoc.defaultView;
   this.inspector = aInspector
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   this._init();
 }
 
 Highlighter.prototype = {
   get selection() {
     return this.inspector.selection;
   },
@@ -134,18 +134,23 @@ Highlighter.prototype = {
     this.selection.on("new-node", this.highlight);
     this.selection.on("new-node", this.updateInfobar);
     this.selection.on("detached", this.highlight);
     this.selection.on("pseudoclass", this.updateInfobar);
     this.selection.on("attribute-changed", this.updateInfobar);
 
     this.onToolSelected = function(event, id) {
       if (id != "inspector") {
+        this.chromeWin.clearTimeout(this.pageEventsMuter);
+        this.detachMouseListeners();
         this.hide();
       } else {
+        if (!this.locked) {
+          this.attachMouseListeners();
+        }
         this.show();
       }
     }.bind(this);
     this.toolbox.on("select", this.onToolSelected);
 
     this.hidden = true;
     this.highlight();
   },
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ b/browser/devtools/inspector/InspectorPanel.jsm
@@ -37,17 +37,17 @@ this.InspectorPanel = function Inspector
   this._target = toolbox._target;
   this.panelDoc = iframeWindow.document;
   this.panelWin = iframeWindow;
   this.panelWin.inspector = this;
 
   this.tabTarget = (this.target.tab != null);
   this.winTarget = (this.target.window != null);
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 InspectorPanel.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   open: function InspectorPanel_open() {
     let deferred = Promise.defer();
--- a/browser/devtools/inspector/Selection.jsm
+++ b/browser/devtools/inspector/Selection.jsm
@@ -53,17 +53,17 @@ this.EXPORTED_SYMBOLS = ["Selection"];
  *
  * @param node Inner node.
  *    Can be null. Can be (un)set in the future via the "node" property;
  * @param trackAttribute Tell if events should be fired when the attributes of
  *    the ndoe change.
  *
  */
 this.Selection = function Selection(node=null, track={attributes:true,detached:true}) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
   this._onMutations = this._onMutations.bind(this);
   this.track = track;
   this.setNode(node);
 }
 
 Selection.prototype = {
   _node: null,
 
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -44,15 +44,15 @@ browser.jar:
     content/browser/devtools/profiler/cleopatra/images/noise.png        (profiler/cleopatra/images/noise.png)
     content/browser/devtools/profiler/cleopatra/images/showall.png      (profiler/cleopatra/images/showall.png)
     content/browser/devtools/profiler/cleopatra/images/throbber.svg     (profiler/cleopatra/images/throbber.svg)
     content/browser/devtools/profiler/cleopatra/images/treetwisty.svg   (profiler/cleopatra/images/treetwisty.svg)
     content/browser/devtools/commandline.css      (commandline/commandline.css)
     content/browser/devtools/commandlineoutput.xhtml  (commandline/commandlineoutput.xhtml)
     content/browser/devtools/commandlinetooltip.xhtml  (commandline/commandlinetooltip.xhtml)
     content/browser/devtools/framework/toolbox-window.xul    (framework/toolbox-window.xul)
-    content/browser/devtools/framework/toolbox.xul           (framework/toolbox.xul)
+*   content/browser/devtools/framework/toolbox.xul           (framework/toolbox.xul)
     content/browser/devtools/framework/toolbox.css           (framework/toolbox.css)
     content/browser/devtools/inspector/inspector.xul         (inspector/inspector.xul)
     content/browser/devtools/inspector/inspector.css         (inspector/inspector.css)
     content/browser/devtools/connect.xhtml  (framework/connect/connect.xhtml)
     content/browser/devtools/connect.css    (framework/connect/connect.css)
     content/browser/devtools/connect.js     (framework/connect/connect.js)
--- a/browser/devtools/markupview/MarkupView.jsm
+++ b/browser/devtools/markupview/MarkupView.jsm
@@ -53,17 +53,17 @@ this.MarkupView = function MarkupView(aI
 
   this._observer = new this.doc.defaultView.MutationObserver(this._mutationObserver.bind(this));
 
   this._boundOnNewSelection = this._onNewSelection.bind(this);
   this._inspector.selection.on("new-node", this._boundOnNewSelection);
   this._onNewSelection();
 
   this._boundKeyDown = this._onKeyDown.bind(this);
-  this._frame.addEventListener("keydown", this._boundKeyDown, false);
+  this._frame.contentWindow.addEventListener("keydown", this._boundKeyDown, false);
 
   this._boundFocus = this._onFocus.bind(this);
   this._frame.addEventListener("focus", this._boundFocus, false);
 
   this._initPreview();
 }
 
 MarkupView.prototype = {
@@ -485,17 +485,17 @@ MarkupView.prototype = {
     delete this._boundFocus;
 
     this._frame.contentWindow.removeEventListener("scroll", this._boundUpdatePreview, true);
     this._frame.contentWindow.removeEventListener("resize", this._boundUpdatePreview, true);
     this._frame.contentWindow.removeEventListener("overflow", this._boundResizePreview, true);
     this._frame.contentWindow.removeEventListener("underflow", this._boundResizePreview, true);
     delete this._boundUpdatePreview;
 
-    this._frame.removeEventListener("keydown", this._boundKeyDown, true);
+    this._frame.contentWindow.removeEventListener("keydown", this._boundKeyDown, true);
     delete this._boundKeyDown;
 
     this._inspector.selection.off("new-node", this._boundOnNewSelection);
     delete this._boundOnNewSelection;
 
     delete this._elt;
 
     delete this._containers;
--- a/browser/devtools/profiler/ProfilerPanel.jsm
+++ b/browser/devtools/profiler/ProfilerPanel.jsm
@@ -35,17 +35,17 @@ XPCOMUtils.defineLazyGetter(this, "Debug
  *   Unique ID for this profile.
  * @param ProfilerPanel panel
  *   A reference to the container panel.
  */
 function ProfileUI(uid, panel) {
   let doc = panel.document;
   let win = panel.window;
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   this.isReady = false;
   this.panel = panel;
   this.uid = uid;
 
   this.iframe = doc.createElement("iframe");
   this.iframe.setAttribute("flex", "1");
   this.iframe.setAttribute("id", "profiler-cleo-" + uid);
@@ -178,17 +178,17 @@ function ProfilerPanel(frame, toolbox) {
   this.window = frame.window;
   this.document = frame.document;
   this.target = toolbox.target;
   this.controller = new ProfilerController();
 
   this.profiles = new Map();
   this._uid = 0;
 
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 ProfilerPanel.prototype = {
   isReady:    null,
   window:     null,
   document:   null,
   target:     null,
   controller: null,
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -64,25 +64,20 @@ this.ResponsiveUIManager = {
         if (aTab.__responsiveUI) {
           aTab.__responsiveUI.close();
         }
         break;
       case "resize toggle":
           this.toggle(aWindow, aTab);
       default:
     }
-  },
+  }
+}
 
-  get events() {
-    if (!this._eventEmitter) {
-      this._eventEmitter = new EventEmitter();
-    }
-    return this._eventEmitter;
-  },
-}
+EventEmitter.decorate(ResponsiveUIManager);
 
 let presets = [
   // Phones
   {key: "320x480", width: 320, height: 480},    // iPhone, B2G, with <meta viewport>
   {key: "360x640", width: 360, height: 640},    // Android 4, phones, with <meta viewport>
 
   // Tablets
   {key: "768x1024", width: 768, height: 1024},   // iPad, with <meta viewport>
@@ -170,17 +165,17 @@ function ResponsiveUI(aWindow, aTab)
     if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
       this.rotate();
     }
   } catch(e) {}
 
   if (this._floatingScrollbars)
     switchToFloatingScrollbars(this.tab);
 
-  ResponsiveUIManager.events.emit("on", this.tab, this);
+  ResponsiveUIManager.emit("on", this.tab, this);
 }
 
 ResponsiveUI.prototype = {
   _transitionsEnabled: true,
   _floatingScrollbars: false, // See bug 799471
   get transitionsEnabled() this._transitionsEnabled,
   set transitionsEnabled(aValue) {
     this._transitionsEnabled = aValue;
@@ -227,17 +222,17 @@ ResponsiveUI.prototype = {
     this.stack.removeChild(this.resizer);
     this.stack.removeChild(this.resizeBar);
 
     // Unset the responsive mode.
     this.container.removeAttribute("responsivemode");
     this.stack.removeAttribute("responsivemode");
 
     delete this.tab.__responsiveUI;
-    ResponsiveUIManager.events.emit("off", this.tab, this);
+    ResponsiveUIManager.emit("off", this.tab, this);
   },
 
   /**
    * Handle keypressed.
    *
    * @param aEvent
    */
   onKeypress: function RUI_onKeypress(aEvent) {
--- a/browser/devtools/responsivedesign/test/browser_responsiveui.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -1,28 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let instance, widthBeforeClose, heightBeforeClose;
-  let events = ResponsiveUI.ResponsiveUIManager.events;
+  let mgr = ResponsiveUI.ResponsiveUIManager;
 
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     waitForFocus(startTest, content);
   }, true);
 
   content.location = "data:text/html,mop";
 
   function startTest() {
     document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
-    events.once("on", function() {executeSoon(onUIOpen)});
+    mgr.once("on", function() {executeSoon(onUIOpen)});
     synthesizeKeyFromKeyTag("key_responsiveUI");
   }
 
   function onUIOpen() {
     // Is it open?
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
@@ -116,36 +116,36 @@ function test() {
     is(content.innerHeight, initialWidth, "The height is now the width.");
     let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
     is(width, initialHeight, "Label updated (width).");
     is(height, initialWidth, "Label updated (height).");
 
     widthBeforeClose = content.innerWidth;
     heightBeforeClose = content.innerHeight;
 
-    events.once("off", function() {executeSoon(restart)});
+    mgr.once("off", function() {executeSoon(restart)});
     EventUtils.synthesizeKey("VK_ESCAPE", {});
   }
 
   function restart() {
-    events.once("on", function() {executeSoon(onUIOpen2)});
+    mgr.once("on", function() {executeSoon(onUIOpen2)});
     synthesizeKeyFromKeyTag("key_responsiveUI");
   }
 
   function onUIOpen2() {
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menus checked");
 
     is(content.innerWidth, widthBeforeClose, "width restored.");
     is(content.innerHeight, heightBeforeClose, "height restored.");
 
-    events.once("off", function() {executeSoon(finishUp)});
+    mgr.once("off", function() {executeSoon(finishUp)});
     EventUtils.synthesizeKey("VK_ESCAPE", {});
   }
 
   function finishUp() {
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
 
--- a/browser/devtools/shared/EventEmitter.jsm
+++ b/browser/devtools/shared/EventEmitter.jsm
@@ -1,29 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["EventEmitter"];
 
 /**
  * EventEmitter.
- *
- * @param Object aObjectToExtend
- *        If aObjectToExtend is not null, the public methods of EventEmitter
- *        are bound to the object.
  */
-this.EventEmitter = function EventEmitter(aObjectToExtend) {
-  if (aObjectToExtend) {
-    aObjectToExtend.on = this.on.bind(this);
-    aObjectToExtend.off = this.off.bind(this);
-    aObjectToExtend.once = this.once.bind(this);
-    aObjectToExtend.emit = this.emit.bind(this);
-  }
-}
+this.EventEmitter = function EventEmitter() {};
+
+/**
+ * Decorate an object with event emitter functionality.
+ *
+ * @param Object aObjectToDecorate
+ *        Bind all public methods of EventEmitter to
+ *        the aObjectToDecorate object.
+ */
+EventEmitter.decorate = function EventEmitter_decorate (aObjectToDecorate) {
+  let emitter = new EventEmitter();
+  aObjectToDecorate.on = emitter.on.bind(emitter);
+  aObjectToDecorate.off = emitter.off.bind(emitter);
+  aObjectToDecorate.once = emitter.once.bind(emitter);
+  aObjectToDecorate.emit = emitter.emit.bind(emitter);
+};
 
 EventEmitter.prototype = {
   /**
    * Connect a listener.
    *
    * @param string aEvent
    *        The event name to which we're connecting.
    * @param function aListener
@@ -98,10 +102,10 @@ EventEmitter.prototype = {
         catch (ex) {
           // Prevent a bad listener from interfering with the others.
           let msg = ex + ": " + ex.stack;
           Components.utils.reportError(msg);
           dump(msg + "\n");
         }
       }
     }
-  },
-}
+  }
+};
--- a/browser/devtools/shared/test/browser_eventemitter_basic.js
+++ b/browser/devtools/shared/test/browser_eventemitter_basic.js
@@ -9,17 +9,17 @@ function test() {
 
 function testEmitter(aObject) {
   Cu.import("resource:///modules/devtools/EventEmitter.jsm", this);
 
   let emitter;
 
   if (aObject) {
     emitter = aObject;
-    new EventEmitter(emitter);
+    EventEmitter.decorate(emitter);
   } else {
     emitter = new EventEmitter();
   }
 
   ok(emitter, "We have an event emitter");
 
   emitter.on("next", next);
   emitter.emit("next", "abc", "def");
--- a/browser/devtools/styleeditor/StyleEditorPanel.jsm
+++ b/browser/devtools/styleeditor/StyleEditorPanel.jsm
@@ -11,17 +11,17 @@ this.EXPORTED_SYMBOLS = ["StyleEditorPan
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/commonjs/promise/core.js");
 Cu.import("resource:///modules/devtools/EventEmitter.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "StyleEditorChrome",
                         "resource:///modules/devtools/StyleEditorChrome.jsm");
 
 this.StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
   this._target = toolbox.target;
 
   this.reset = this.reset.bind(this);
   this.newPage = this.newPage.bind(this);
   this.destroy = this.destroy.bind(this);
 
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -172,20 +172,23 @@ TiltVisualizer.prototype = {
                              false);
     Services.obs.addObserver(this.onNewNodeFromTilt,
                              this.presenter.NOTIFICATIONS.UNHIGHLIGHTING,
                              false);
 
     let target = TargetFactory.forTab(aTab);
     let toolbox = gDevTools.getToolbox(target);
     if (toolbox) {
-      this.inspector = toolbox.getPanel("inspector");
-      this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
-      this.inspector.selection.on("detached", this.onNewNodeFromInspector);
-      this.onNewNodeFromInspector();
+      let panel = toolbox.getPanel("inspector");
+      if (panel) {
+        this.inspector = panel;
+        this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
+        this.inspector.selection.on("detached", this.onNewNodeFromInspector);
+        this.onNewNodeFromInspector();
+      }
     }
   },
 
   /**
    * Unregister inspector event listeners.
    */
   unbindInspector: function TV_unbindInspector()
   {
--- a/browser/devtools/webconsole/WebConsolePanel.jsm
+++ b/browser/devtools/webconsole/WebConsolePanel.jsm
@@ -17,17 +17,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/HUDService.jsm");
 
 /**
  * A DevToolPanel that controls the Web Console.
  */
 function WebConsolePanel(iframeWindow, toolbox) {
   this._frameWindow = iframeWindow;
   this._toolbox = toolbox;
-  new EventEmitter(this);
+  EventEmitter.decorate(this);
 }
 
 WebConsolePanel.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   open: function StyleEditor_open() {
     let tab = this._toolbox._getHostTab();
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -112,16 +112,22 @@ MOCHITEST_BROWSER_FILES = \
 	browser_output_breaks_after_console_dir_uninspectable.js \
 	browser_console_log_inspectable_object.js \
 	browser_bug_638949_copy_link_location.js \
 	browser_output_longstring_expand.js \
 	browser_netpanel_longstring_expand.js \
 	head.js \
 	$(NULL)
 
+ifeq ($(OS_ARCH), Darwin)
+MOCHITEST_BROWSER_FILES += \
+        browser_webconsole_bug_804845_ctrl_key_nav.js \
+        $(NULL)
+endif
+
 ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 MOCHITEST_BROWSER_FILES += \
         browser_webconsole_bug_618311_private_browsing.js \
         $(NULL)
 endif
 
 MOCHITEST_BROWSER_FILES += \
 	test-console.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js
@@ -0,0 +1,214 @@
+/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *  zmgmoz <zmgmoz@gmail.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// Test navigation of webconsole contents via ctrl-a, ctrl-e, ctrl-p, ctrl-n
+// see https://bugzilla.mozilla.org/show_bug.cgi?id=804845
+
+let jsterm, inputNode;
+function test() {
+  addTab("data:text/html;charset=utf-8,Web Console test for bug 804845 and bug 619598");
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, doTests);
+  }, true);
+}
+
+function doTests(HUD) {
+  jsterm = HUD.jsterm;
+  inputNode = jsterm.inputNode;
+  ok(!jsterm.inputNode.value, "inputNode.value is empty");
+  is(jsterm.inputNode.selectionStart, 0);
+  is(jsterm.inputNode.selectionEnd, 0);
+
+  testSingleLineInputNavNoHistory();
+  testMultiLineInputNavNoHistory();
+  testNavWithHistory();
+
+  jsterm = inputNode = null;
+  executeSoon(finishTest);
+}
+
+function testSingleLineInputNavNoHistory() {
+  // Single char input
+  EventUtils.synthesizeKey("1", {});
+  is(inputNode.selectionStart, 1, "caret location after single char input");
+
+  // nav to start/end with ctrl-a and ctrl-e;
+  EventUtils.synthesizeKey("a", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "caret location after single char input and ctrl-a");
+
+  EventUtils.synthesizeKey("e", { ctrlKey: true });
+  is(inputNode.selectionStart, 1, "caret location after single char input and ctrl-e");
+
+  // Second char input
+  EventUtils.synthesizeKey("2", {});
+  // nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n
+  EventUtils.synthesizeKey("VK_UP", {});
+  is(inputNode.selectionStart, 0, "caret location after two char input and VK_UP");
+  EventUtils.synthesizeKey("VK_DOWN", {});
+  is(inputNode.selectionStart, 2, "caret location after two char input and VK_DOWN");
+
+  EventUtils.synthesizeKey("a", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "move caret to beginning of 2 char input with ctrl-a");
+  EventUtils.synthesizeKey("a", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "no change of caret location on repeat ctrl-a");
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "no change of caret location on ctrl-p from beginning of line");
+
+  EventUtils.synthesizeKey("e", { ctrlKey: true });
+  is(inputNode.selectionStart, 2, "move caret to end of 2 char input with ctrl-e");
+  EventUtils.synthesizeKey("e", { ctrlKey: true });
+  is(inputNode.selectionStart, 2, "no change of caret location on repeat ctrl-e");
+  EventUtils.synthesizeKey("n", { ctrlKey: true });
+  is(inputNode.selectionStart, 2, "no change of caret location on ctrl-n from end of line");
+
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "ctrl-p moves to start of line");
+
+  EventUtils.synthesizeKey("n", { ctrlKey: true });
+  is(inputNode.selectionStart, 2, "ctrl-n moves to end of line");
+}
+
+function testMultiLineInputNavNoHistory() {
+  let lineValues = ["one", "2", "something longer", "", "", "three!"];
+  jsterm.setInputValue("");
+  // simulate shift-return
+  for (let i = 0; i < lineValues.length; i++) {
+    jsterm.setInputValue(inputNode.value + lineValues[i]);
+    EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
+  }
+  let inputValue = inputNode.value;
+  is(inputNode.selectionStart, inputNode.selectionEnd);
+  is(inputNode.selectionStart, inputValue.length, "caret at end of multiline input");
+
+  // possibility newline is represented by one ('\r', '\n') or two ('\r\n') chars
+  let newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0];
+
+  // Ok, test navigating within the multi-line string!
+  EventUtils.synthesizeKey("VK_UP", {});
+  let expectedStringAfterCarat = lineValues[5]+newlineString;
+  is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
+     "up arrow from end of multiline");
+
+  EventUtils.synthesizeKey("VK_DOWN", {});
+  is(inputNode.value.slice(inputNode.selectionStart), "",
+     "down arrow from within multiline");
+
+  // navigate up through input lines
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
+     "ctrl-p from end of multiline");
+
+  for (let i = 4; i >= 0; i--) {
+    EventUtils.synthesizeKey("p", { ctrlKey: true });
+    expectedStringAfterCarat = lineValues[i] + newlineString + expectedStringAfterCarat;
+    is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
+       "ctrl-p from within line " + i + " of multiline input");
+  }
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.selectionStart, 0, "reached start of input");
+  is(inputNode.value, inputValue,
+     "no change to multiline input on ctrl-p from beginning of multiline");
+
+  // navigate to end of first line
+  EventUtils.synthesizeKey("e", { ctrlKey: true });
+  let caretPos = inputNode.selectionStart;
+  let expectedStringBeforeCarat = lineValues[0];
+  is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
+     "ctrl-e into multiline input");
+  EventUtils.synthesizeKey("e", { ctrlKey: true });
+  is(inputNode.selectionStart, caretPos,
+     "repeat ctrl-e doesn't change caret position in multiline input");
+
+  // navigate down one line; ctrl-a to the beginning; ctrl-e to end
+  for (let i = 1; i < lineValues.length; i++) {
+    EventUtils.synthesizeKey("n", { ctrlKey: true });
+    EventUtils.synthesizeKey("a", { ctrlKey: true });
+    caretPos = inputNode.selectionStart;
+    expectedStringBeforeCarat += newlineString;
+    is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
+       "ctrl-a to beginning of line " + (i+1) + " in multiline input");
+
+    EventUtils.synthesizeKey("e", { ctrlKey: true });
+    caretPos = inputNode.selectionStart;
+    expectedStringBeforeCarat += lineValues[i];
+    is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
+       "ctrl-e to end of line " + (i+1) + "in multiline input");
+  }
+}
+
+function testNavWithHistory() {
+  // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
+  // caret placed _within_ single line input
+  let values = ['"single line input"',
+                '"a longer single-line input to check caret repositioning"',
+                ['"multi-line"', '"input"', '"here!"'].join("\n"),
+               ];
+  // submit to history
+  for (let i = 0; i < values.length; i++) {
+    jsterm.setInputValue(values[i]);
+    jsterm.execute();
+  }
+  is(inputNode.selectionStart, 0, "caret location at start of empty line");
+
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.selectionStart, values[values.length-1].length,
+     "caret location correct at end of last history input");
+
+  // Navigate backwards history with ctrl-p
+  for (let i = values.length-1; i > 0; i--) {
+    let match = values[i].match(/(\n)/g);
+    if (match) {
+      // multi-line inputs won't update from history unless caret at beginning
+      EventUtils.synthesizeKey("a", { ctrlKey: true });
+      for (let i = 0; i < match.length; i++) {
+        EventUtils.synthesizeKey("p", { ctrlKey: true });
+      }
+      EventUtils.synthesizeKey("p", { ctrlKey: true });
+    } else {
+      // single-line inputs will update from history from end of line
+      EventUtils.synthesizeKey("p", { ctrlKey: true });
+    }
+    is(inputNode.value, values[i-1],
+       "ctrl-p updates inputNode from backwards history values[" + i-1 + "]");
+  }
+  let inputValue = inputNode.value;
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.selectionStart, 0,
+     "ctrl-p at beginning of history moves caret location to beginning of line");
+  is(inputNode.value, inputValue,
+     "no change to input value on ctrl-p from beginning of line");
+
+  // Navigate forwards history with ctrl-n
+  for (let i = 1; i<values.length; i++) {
+    EventUtils.synthesizeKey("n", { ctrlKey: true });
+    is(inputNode.value, values[i],
+       "ctrl-n updates inputNode from forwards history values[" + i + "]");
+    is(inputNode.selectionStart, values[i].length,
+       "caret location correct at end of history input for values[" + i + "]");
+  }
+  EventUtils.synthesizeKey("n", { ctrlKey: true });
+  ok(!inputNode.value, "ctrl-n at end of history updates to empty input");
+
+  // Simulate editing multi-line
+  inputValue = "one\nlinebreak";
+  jsterm.setInputValue(inputValue);
+
+  // Attempt nav within input
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.value, inputValue,
+     "ctrl-p from end of multi-line does not trigger history");
+
+  EventUtils.synthesizeKey("a", { ctrlKey: true });
+  EventUtils.synthesizeKey("p", { ctrlKey: true });
+  is(inputNode.value, values[values.length-1],
+     "ctrl-p from start of multi-line triggers history");
+}
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -373,16 +373,17 @@ WebConsoleFrame.prototype = {
     this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);
 
     let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
     this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this._connectTimer.initWithCallback(this._connectionTimeout,
                                         timeout, Ci.nsITimer.TYPE_ONE_SHOT);
 
     this.proxy.connect(function() {
+      // Don't complete connection if the connection timed-out.
       if (this._connectTimer) {
         this._connectTimer.cancel();
         this._connectTimer = null;
         this.saveRequestAndResponseBodies = this._saveRequestAndResponseBodies;
         this._onInitComplete();
       }
     }.bind(this));
   },
@@ -3176,31 +3177,82 @@ JSTerm.prototype = {
   /**
    * The inputNode "keypress" event handler.
    *
    * @param nsIDOMEvent aEvent
    */
   keyPress: function JSTF_keyPress(aEvent)
   {
     if (aEvent.ctrlKey) {
+      let inputNode = this.inputNode;
+      let closePopup = false;
       switch (aEvent.charCode) {
         case 97:
           // control-a
-          this.inputNode.setSelectionRange(0, 0);
+          let lineBeginPos = 0;
+          if (this.hasMultilineInput()) {
+            // find index of closest newline <= to cursor
+            for (let i = inputNode.selectionStart-1; i >= 0; i--) {
+              if (inputNode.value.charAt(i) == "\r" ||
+                  inputNode.value.charAt(i) == "\n") {
+                lineBeginPos = i+1;
+                break;
+              }
+            }
+          }
+          inputNode.setSelectionRange(lineBeginPos, lineBeginPos);
           aEvent.preventDefault();
+          closePopup = true;
           break;
         case 101:
           // control-e
-          this.inputNode.setSelectionRange(this.inputNode.value.length,
-                                           this.inputNode.value.length);
+          let lineEndPos = inputNode.value.length;
+          if (this.hasMultilineInput()) {
+            // find index of closest newline >= cursor
+            for (let i = inputNode.selectionEnd; i<lineEndPos; i++) {
+              if (inputNode.value.charAt(i) == "\r" ||
+                  inputNode.value.charAt(i) == "\n") {
+                lineEndPos = i;
+                break;
+              }
+            }
+          }
+          inputNode.setSelectionRange(lineEndPos, lineEndPos);
           aEvent.preventDefault();
           break;
+        case 110:
+          // Control-N differs from down arrow: it ignores autocomplete state.
+          // Note that we preserve the default 'down' navigation within
+          // multiline text.
+          if (Services.appinfo.OS == "Darwin" &&
+              this.canCaretGoNext() &&
+              this.historyPeruse(HISTORY_FORWARD)) {
+            aEvent.preventDefault();
+          }
+          closePopup = true;
+          break;
+        case 112:
+          // Control-P differs from up arrow: it ignores autocomplete state.
+          // Note that we preserve the default 'up' navigation within
+          // multiline text.
+          if (Services.appinfo.OS == "Darwin" &&
+              this.canCaretGoPrevious() &&
+              this.historyPeruse(HISTORY_BACK)) {
+            aEvent.preventDefault();
+          }
+          closePopup = true;
+          break;
         default:
           break;
       }
+      if (closePopup) {
+        if (this.autocompletePopup.isOpen) {
+          this.clearCompletion();
+        }
+      }
       return;
     }
     else if (aEvent.shiftKey &&
         aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
       // shift return
       // TODO: expand the inputNode height by one line
       return;
     }
@@ -3312,16 +3364,27 @@ JSTerm.prototype = {
     else {
       throw new Error("Invalid argument 0");
     }
 
     return true;
   },
 
   /**
+   * Test for multiline input.
+   *
+   * @return boolean
+   *         True if CR or LF found in node value; else false.
+   */
+  hasMultilineInput: function JST_hasMultilineInput()
+  {
+    return /[\r\n]/.test(this.inputNode.value);
+  },
+
+  /**
    * Check if the caret is at a location that allows selecting the previous item
    * in history when the user presses the Up arrow key.
    *
    * @return boolean
    *         True if the caret is at a location that allows selecting the
    *         previous item in history when the user presses the Up arrow key,
    *         otherwise false.
    */
@@ -3978,59 +4041,79 @@ function WebConsoleConnectionProxy(aWebC
   this.owner = aWebConsole;
   this.target = aTarget;
 
   this._onPageError = this._onPageError.bind(this);
   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   this._onFileActivity = this._onFileActivity.bind(this);
-  this._onLocationChange = this._onLocationChange.bind(this);
+  this._onTabNavigated = this._onTabNavigated.bind(this);
 }
 
 WebConsoleConnectionProxy.prototype = {
   /**
    * The owning Web Console instance.
    *
    * @see WebConsoleFrame
    * @type object
    */
   owner: null,
 
   /**
+   * The target that the console connects to.
+   * @type RemoteTarget
+   */
+  target: null,
+
+  /**
    * The DebuggerClient object.
    *
    * @see DebuggerClient
    * @type object
    */
   client: null,
 
   /**
    * The WebConsoleClient object.
    *
    * @see WebConsoleClient
    * @type object
    */
   webConsoleClient: null,
 
   /**
+   * The TabClient instance we use.
+   * @type object
+   */
+  tabClient: null,
+
+  /**
    * Tells if the connection is established.
    * @type boolean
    */
   connected: false,
 
   /**
    * The WebConsoleActor ID.
    *
    * @private
    * @type string
    */
   _consoleActor: null,
 
   /**
+   * The TabActor ID.
+   *
+   * @private
+   * @type string
+   */
+  _tabActor: null,
+
+  /**
    * Tells if the window.console object of the remote web page is the native
    * object or not.
    * @private
    * @type boolean
    */
   _hasNativeConsoleAPI: false,
 
   /**
@@ -4064,59 +4147,108 @@ WebConsoleConnectionProxy.prototype = {
       client = this.client = new DebuggerClient(transport);
     }
 
     client.addListener("pageError", this._onPageError);
     client.addListener("consoleAPICall", this._onConsoleAPICall);
     client.addListener("networkEvent", this._onNetworkEvent);
     client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
     client.addListener("fileActivity", this._onFileActivity);
-    client.addListener("locationChange", this._onLocationChange);
+    client.addListener("tabNavigated", this._onTabNavigated);
 
     if (this.target.isRemote) {
-      this._consoleActor = this.target.form.consoleActor;
       if (!this.target.chrome) {
-        this.owner.onLocationChange(this.target.url, this.target.name);
+        // target.form is a TabActor grip
+        this._attachTab(this.target.form, aCallback);
       }
-
-      let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
-                       "FileActivity", "LocationChange"];
-      this.client.attachConsole(this._consoleActor, listeners,
-                                this._onAttachConsole.bind(this, aCallback));
-      return;
+      else {
+        // target.form is a RootActor grip
+        this._consoleActor = this.target.form.consoleActor;
+        this._attachConsole(aCallback);
+      }
     }
-    client.connect(function(aType, aTraits) {
-      client.listTabs(this._onListTabs.bind(this, aCallback));
-    }.bind(this));
+    else {
+      client.connect(function(aType, aTraits) {
+        client.listTabs(this._onListTabs.bind(this, aCallback));
+      }.bind(this));
+    }
   },
 
   /**
    * The "listTabs" response handler.
    *
    * @private
    * @param function [aCallback]
    *        Optional function to invoke once the connection is established.
    * @param object aResponse
    *        The JSON response object received from the server.
    */
   _onListTabs: function WCCP__onListTabs(aCallback, aResponse)
   {
-    let selectedTab = aResponse.tabs[aResponse.selected];
-    if (selectedTab) {
-      this._consoleActor = selectedTab.consoleActor;
-      this.owner.onLocationChange(selectedTab.url, selectedTab.title);
+    if (aResponse.error) {
+      Cu.reportError("listTabs failed: " + aResponse.error + " " +
+                     aResponse.message);
+      return;
     }
-    else {
-      this._consoleActor = aResponse.consoleActor;
+
+    this._attachTab(aResponse.tabs[aResponse.selected], aCallback);
+  },
+
+  /**
+   * Attach to the tab actor.
+   *
+   * @private
+   * @param object aTab
+   *        Grip for the tab to attach to.
+   * @param function aCallback
+   *        Function to invoke when the connection is established.
+   */
+  _attachTab: function WCCP__attachTab(aTab, aCallback)
+  {
+    this._consoleActor = aTab.consoleActor;
+    this._tabActor = aTab.actor;
+    this.owner.onLocationChange(aTab.url, aTab.title);
+    this.client.attachTab(this._tabActor,
+                          this._onAttachTab.bind(this, aCallback));
+  },
+
+  /**
+   * The "attachTab" response handler.
+   *
+   * @private
+   * @param function [aCallback]
+   *        Optional function to invoke once the connection is established.
+   * @param object aResponse
+   *        The JSON response object received from the server.
+   * @param object aTabClient
+   *        The TabClient instance for the attached tab.
+   */
+  _onAttachTab: function WCCP__onAttachTab(aCallback, aResponse, aTabClient)
+  {
+    if (aResponse.error) {
+      Cu.reportError("attachTab failed: " + aResponse.error + " " +
+                     aResponse.message);
+      return;
     }
 
-    this.owner._resetConnectionTimeout();
-
+    this.tabClient = aTabClient;
+    this._attachConsole(aCallback);
+  },
+
+  /**
+   * Attach to the Web Console actor.
+   *
+   * @private
+   * @param function aCallback
+   *        Function to invoke when the connection is established.
+   */
+  _attachConsole: function WCCP__attachConsole(aCallback)
+  {
     let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
-                     "FileActivity", "LocationChange"];
+                     "FileActivity"];
     this.client.attachConsole(this._consoleActor, listeners,
                               this._onAttachConsole.bind(this, aCallback));
   },
 
   /**
    * The "attachConsole" response handler.
    *
    * @private
@@ -4255,32 +4387,35 @@ WebConsoleConnectionProxy.prototype = {
   _onFileActivity: function WCCP__onFileActivity(aType, aPacket)
   {
     if (this.owner && aPacket.from == this._consoleActor) {
       this.owner.handleFileActivity(aPacket.uri);
     }
   },
 
   /**
-   * The "locationChange" message type handler. We redirect any message to
+   * The "tabNavigated" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param string aType
    *        Message type.
    * @param object aPacket
    *        The message received from the server.
    */
-  _onLocationChange: function WCCP__onLocationChange(aType, aPacket)
+  _onTabNavigated: function WCCP__onTabNavigated(aType, aPacket)
   {
-    if (!this.owner || aPacket.from != this._consoleActor) {
+    if (!this.owner || aPacket.from != this._tabActor) {
       return;
     }
 
-    this.owner.onLocationChange(aPacket.uri, aPacket.title);
+    if (aPacket.url) {
+      this.owner.onLocationChange(aPacket.url, aPacket.title);
+    }
+
     if (aPacket.state == "stop" && !aPacket.nativeConsoleAPI) {
       this.owner.logWarningAboutReplacedAPI();
     }
   },
 
   /**
    * Release an object actor.
    *
@@ -4314,43 +4449,52 @@ WebConsoleConnectionProxy.prototype = {
       }
       if (aOnDisconnect) {
         aOnDisconnect();
         aOnDisconnect = null;
       }
     };
 
     let timer = null;
-    if (aOnDisconnect) {
+    let remoteTarget = this.target.isRemote;
+    if (aOnDisconnect && !remoteTarget) {
       timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       timer.initWithCallback(onDisconnect, 1500, Ci.nsITimer.TYPE_ONE_SHOT);
     }
 
     this.client.removeListener("pageError", this._onPageError);
     this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
     this.client.removeListener("networkEvent", this._onNetworkEvent);
     this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
     this.client.removeListener("fileActivity", this._onFileActivity);
-    this.client.removeListener("locationChange", this._onLocationChange);
+    this.client.removeListener("tabNavigated", this._onTabNavigated);
+
+    let client = this.client;
+
+    this.client = null;
+    this.webConsoleClient = null;
+    this.tabClient = null;
+    this.target = null;
+    this.connected = false;
+    this.owner = null;
 
     try {
-      if (!this.target.isRemote) {
-        this.client.close(onDisconnect);
+      if (!remoteTarget) {
+        client.close(onDisconnect);
       }
     }
     catch (ex) {
       Cu.reportError("Web Console disconnect exception: " + ex);
       Cu.reportError(ex.stack);
       onDisconnect();
     }
 
-    this.client = null;
-    this.webConsoleClient = null;
-    this.connected = false;
-    this.owner = null;
+    if (remoteTarget) {
+      onDisconnect();
+    }
   },
 };
 
 function gSequenceId()
 {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
--- a/browser/modules/NewTabUtils.jsm
+++ b/browser/modules/NewTabUtils.jsm
@@ -534,17 +534,17 @@ let PlacesProvider = {
     options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_DESCENDING
 
     let links = [];
 
     let callback = {
       handleResult: function (aResultSet) {
         let row;
 
-        while (row = aResultSet.getNextRow()) {
+        while ((row = aResultSet.getNextRow())) {
           let url = row.getResultByIndex(1);
           if (LinkChecker.checkLoadURI(url)) {
             let title = row.getResultByIndex(2);
             links.push({url: url, title: title});
           }
         }
       },
 
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -52,16 +52,22 @@ GRE_MILESTONE = $(shell tail -n 1 $(tops
 APP_INI_DEPS = $(topsrcdir)/config/milestone.txt
 endif
 
 APP_BUILDID := $(shell cat $(DEPTH)/config/buildid)
 APP_INI_DEPS += $(DEPTH)/config/buildid
 
 DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID)
 
+# Set a flag that can be used in pref files to disable features if
+# we are not building for Aurora or Nightly.
+ifeq (,$(findstring a,$(GRE_MILESTONE)))
+PREF_PPFLAGS += -DRELEASE_BUILD
+endif
+
 DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)"
 APP_INI_DEPS += $(DEPTH)/config/autoconf.mk
 
 MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template="{node|short}\n" 2>/dev/null))
 ifdef MOZ_SOURCE_STAMP
 DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)"
 endif
 
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -49,17 +49,17 @@ MOZ_ARG_WITH_STRING(android-platform,
                            location of platform dir],
     android_platform=$withval)
 
 case "$target" in
 arm-linux*-android*|*-linuxandroid*)
     android_tool_prefix="arm-linux-androideabi"
     ;;
 i?86-*android*)
-    android_tool_prefix="i686-android-linux"
+    android_tool_prefix="i686-linux-android"
     ;;
 mipsel-*android*)
     android_tool_prefix="mipsel-linux-android"
     ;;
 *)
     android_tool_prefix="$target_os"
     ;;
 esac
@@ -114,16 +114,27 @@ case "$target" in
 
         if test -d "$android_platform" ; then
             AC_MSG_RESULT([$android_platform])
         else
             AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.])
         fi
     fi
 
+    dnl Old NDK support. If minimum requirement is changed to NDK r8b,
+    dnl please remove this.
+    case "$target_cpu" in
+    i?86)
+        if ! test -e "$android_toolchain"/bin/"$android_tool_prefix"-gcc; then
+            dnl Old NDK toolchain name
+            android_tool_prefix="i686-android-linux"
+        fi
+        ;;
+    esac
+
     dnl set up compilers
     TOOLCHAIN_PREFIX="$android_toolchain/bin/$android_tool_prefix-"
     AS="$android_toolchain"/bin/"$android_tool_prefix"-as
     CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc
     CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++
     CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp
     LD="$android_toolchain"/bin/"$android_tool_prefix"-ld
     AR="$android_toolchain"/bin/"$android_tool_prefix"-ar
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -605,16 +605,32 @@ user_pref("camino.use_system_proxy_setti
       {
         'name': 'https_example_csp_privileged',
         'csp': "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'",
         'origin': 'https://example.com',
         'manifestURL': 'https://example.com/manifest_csp_priv.webapp',
         'description': 'https://example.com Privileged App with manifest policy',
         'appStatus': _APP_STATUS_PRIVILEGED
       }, 
+      {
+        'name': 'https_a_domain_certified',
+        'csp' : "",
+        'origin': 'https://acertified.com',
+        'manifestURL': 'https://acertified.com/manifest.webapp',
+        'description': 'https://acertified.com Certified App',
+        'appStatus': _APP_STATUS_CERTIFIED
+      }, 
+      {
+        'name': 'https_a_domain_privileged',
+        'csp': "",
+        'origin': 'https://aprivileged.com',
+        'manifestURL': 'https://aprivileged.com/manifest.webapp',
+        'description': 'https://aprivileged.com Privileged App ',
+        'appStatus': _APP_STATUS_PRIVILEGED
+      }, 
     ];
     self.setupTestApps(profileDir, apps)
 
   def addCommonOptions(self, parser):
     "Adds command-line options which are common to mochitest and reftest."
 
     parser.add_option("--setpref",
                       action = "append", type = "string",
--- a/build/unix/build-clang/build-clang.py
+++ b/build/unix/build-clang/build-clang.py
@@ -1,14 +1,14 @@
 #!/usr/bin/python
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-llvm_revision = "169730"
+llvm_revision = "170377"
 moz_version = "moz0"
 
 ##############################################
 
 import os
 import os.path
 import shutil
 import tarfile
--- a/configure.in
+++ b/configure.in
@@ -4239,16 +4239,17 @@ MOZ_VORBIS=
 MOZ_TREMOR=
 MOZ_WAVE=1
 MOZ_SAMPLE_TYPE_FLOAT32=
 MOZ_SAMPLE_TYPE_S16=
 MOZ_MEDIA=
 MOZ_OPUS=1
 MOZ_WEBM=1
 MOZ_DASH=1
+MOZ_WMF=
 MOZ_WEBRTC=1
 MOZ_PEERCONNECTION=
 MOZ_SRTP=
 MOZ_WEBRTC_SIGNALING=
 MOZ_WEBRTC_IN_LIBXUL=
 MOZ_SCTP=
 MOZ_MEDIA_PLUGINS=
 MOZ_MEDIA_NAVIGATOR=
@@ -5414,16 +5415,39 @@ if test -n "$MOZ_DASH"; then
         AC_DEFINE(MOZ_DASH)
     else
         dnl Fail if WebM is not enabled as well as DASH.
         AC_MSG_ERROR([WebM is currently disabled and must be enabled for DASH
                      to work.])
     fi
 fi;
 
+
+dnl ========================================================
+dnl = Windows Media Foundation support
+dnl ========================================================
+if test "$OS_ARCH" = "WINNT"; then
+    dnl Enable Windows Media Foundation support by default.
+    dnl Note our minimum SDK version is Windows 7 SDK, so we are (currently)
+    dnl guaranteed to have a recent-enough SDK to build WMF.
+    MOZ_WMF=1
+fi
+
+MOZ_ARG_DISABLE_BOOL(wmf,
+[  --disable-wmf  Disable support for Windows Media Foundation],
+    MOZ_WMF=,
+    MOZ_WMF=1)
+
+if test -n "$MOZ_WMF"; then
+    AC_DEFINE(MOZ_WMF)
+    MOZ_SYDNEYAUDIO=1
+    MOZ_CUBEB=1
+    MOZ_MEDIA=1
+fi;
+
 dnl ========================================================
 dnl = Enable media plugin support
 dnl ========================================================
 if test "$OS_TARGET" = Android -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then
   dnl Enable support on android by default
   MOZ_MEDIA_PLUGINS=1
 fi
 
@@ -8826,16 +8850,17 @@ AC_SUBST(MOZ_SPEEX_RESAMPLER)
 AC_SUBST(MOZ_SOUNDTOUCH)
 AC_SUBST(MOZ_CUBEB)
 AC_SUBST(MOZ_WAVE)
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_OPUS)
 AC_SUBST(MOZ_WEBM)
 AC_SUBST(MOZ_DASH)
+AC_SUBST(MOZ_WMF)
 AC_SUBST(MOZ_MEDIA_PLUGINS)
 AC_SUBST(MOZ_OMX_PLUGIN)
 AC_SUBST(MOZ_VP8_ERROR_CONCEALMENT)
 AC_SUBST(MOZ_VP8_ENCODER)
 AC_SUBST(MOZ_VP8)
 AC_SUBST(MOZ_OGG)
 AC_SUBST(VPX_AS)
 AC_SUBST(VPX_ASFLAGS)
--- a/content/base/public/nsIImageLoadingContent.idl
+++ b/content/base/public/nsIImageLoadingContent.idl
@@ -106,17 +106,17 @@ interface nsIImageLoadingContent : imgIN
   /**
    * Used to notify the image loading content node that a frame has been
    * destroyed.
    */
   [notxpcom] void frameDestroyed(in nsIFrame aFrame);
 
   /**
    * Used to find out what type of request one is dealing with (eg
-   * which request got passed through to the imgIDecoderObserver
+   * which request got passed through to the imgINotificationObserver
    * interface of an observer)
    *
    * @param aRequest the request whose type we want to know
    *
    * @return an enum value saying what type this request is
    *
    * @throws NS_ERROR_UNEXPECTED if aRequest is not known
    */
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -14,22 +14,21 @@
 #include "nscore.h"
 #include "nsStringGlue.h"
 #include "nsStringBuffer.h"
 #include "nsColor.h"
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
 #include "SVGAttrValueWrapper.h"
+#include "nsTArrayForwardDeclare.h"
 
 class nsAString;
 class nsIAtom;
 class nsIDocument;
-template<class E, class A> class nsTArray;
-struct nsTArrayDefaultAllocator;
 class nsStyledElementNotElementCSSInlineStyle;
 struct MiscContainer;
 
 namespace mozilla {
 namespace css {
 class StyleRule;
 struct URLValue;
 struct ImageValue;
@@ -421,17 +420,17 @@ private:
                           nsresult* aErrorCode,
                           bool aCanBePercent = false,
                           bool* aIsPercent = nullptr) const;
   // Given an enum table and a particular entry in that table, return
   // the actual integer value we should store.
   int32_t EnumTableEntryToValue(const EnumTable* aEnumTable,
                                 const EnumTable* aTableEntry);  
 
-  static nsTArray<const EnumTable*, nsTArrayDefaultAllocator>* sEnumTableArray;
+  static nsTArray<const EnumTable*>* sEnumTableArray;
 
   uintptr_t mBits;
 };
 
 inline const nsAttrValue&
 nsAttrValue::operator=(const nsAttrValue& aOther)
 {
   SetTo(aOther);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6540,16 +6540,27 @@ nsContentUtils::FindInternalContentViewe
       DecoderTraits::IsMediaPluginsType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif // MOZ_MEDIA_PLUGINS
+
+#ifdef MOZ_WMF
+  if (DecoderTraits::IsWMFSupportedType(nsDependentCString(aType))) {
+    docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
+    if (docFactory && aLoaderType) {
+      *aLoaderType = TYPE_CONTENT;
+    }
+    return docFactory.forget();
+  }
+#endif
+
 #endif // MOZ_MEDIA
 
   return NULL;
 }
 
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -2059,16 +2059,24 @@ nsFrameLoader::TryRemoteBrowser()
     context.SetTabContextForBrowserFrame(containingApp, scrollingBehavior);
   }
 
   mRemoteBrowser = ContentParent::CreateBrowserOrApp(context);
   if (mRemoteBrowser) {
     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mOwnerContent);
     mRemoteBrowser->SetOwnerElement(element);
 
+    // If we're an app, send the frame element's mozapptype down to the child
+    // process.  This ends up in TabChild::GetAppType().
+    if (ownApp) {
+      nsAutoString appType;
+      mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapptype, appType);
+      mRemoteBrowser->SendSetAppType(appType);
+    }
+
     nsCOMPtr<nsIDocShellTreeItem> rootItem;
     parentAsItem->GetRootTreeItem(getter_AddRefs(rootItem));
     nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem);
     nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
     NS_ABORT_IF_FALSE(rootChromeWin, "How did we not get a chrome window here?");
 
     nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
     rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -84,16 +84,17 @@ GK_ATOM(always, "always")
 GK_ATOM(ancestor, "ancestor")
 GK_ATOM(ancestorOrSelf, "ancestor-or-self")
 GK_ATOM(_and, "and")
 GK_ATOM(any, "any")
 GK_ATOM(mozapp, "mozapp")
 GK_ATOM(applet, "applet")
 GK_ATOM(applyImports, "apply-imports")
 GK_ATOM(applyTemplates, "apply-templates")
+GK_ATOM(mozapptype, "mozapptype")
 GK_ATOM(archive, "archive")
 GK_ATOM(area, "area")
 GK_ATOM(article, "article")
 GK_ATOM(ascending, "ascending")
 GK_ATOM(aside, "aside")
 GK_ATOM(aspectRatio, "aspect-ratio")
 GK_ATOM(assign, "assign")
 GK_ATOM(async, "async")
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -45,26 +45,31 @@ nsInProcessTabChildGlobal::DoSendSyncMes
 }
 
 class nsAsyncMessageToParent : public nsRunnable
 {
 public:
   nsAsyncMessageToParent(nsInProcessTabChildGlobal* aTabChild,
                          const nsAString& aMessage,
                          const StructuredCloneData& aData)
-    : mTabChild(aTabChild), mMessage(aMessage)
+    : mTabChild(aTabChild), mMessage(aMessage), mRun(false)
   {
     if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
       NS_RUNTIMEABORT("OOM");
     }
     mClosure = aData.mClosure;
   }
 
   NS_IMETHOD Run()
   {
+    if (mRun) {
+      return NS_OK;
+    }
+
+    mRun = true;
     mTabChild->mASyncMessages.RemoveElement(this);
     if (mTabChild->mChromeMessageManager) {
       StructuredCloneData data;
       data.mData = mData.data();
       data.mDataLength = mData.nbytes();
       data.mClosure = mClosure;
 
       nsRefPtr<nsFrameMessageManager> mm = mTabChild->mChromeMessageManager;
@@ -72,16 +77,19 @@ public:
                          nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
   nsString mMessage;
   JSAutoStructuredCloneBuffer mData;
   StructuredCloneClosure mClosure;
+  // True if this runnable has already been called. This can happen if DoSendSyncMessage
+  // is called while waiting for an asynchronous message send.
+  bool mRun;
 };
 
 bool
 nsInProcessTabChildGlobal::DoSendAsyncMessage(const nsAString& aMessage,
                                               const StructuredCloneData& aData)
 {
   nsCOMPtr<nsIRunnable> ev =
     new nsAsyncMessageToParent(this, aMessage, aData);
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2543,17 +2543,17 @@ CanvasRenderingContext2D::DrawOrMeasureT
       *aWidth = 0;
     }
     return NS_OK;
   }
 
   const ContextState &state = CurrentState();
 
   // This is only needed to know if we can know the drawing bounding box easily.
-  bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
+  bool doDrawShadow = NeedToDrawShadow();
 
   CanvasBidiProcessor processor;
 
   GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
   processor.mPt = gfxPoint(aX, aY);
   processor.mThebes =
     new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
 
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1059,25 +1059,26 @@ WebGLContext::DeleteTexture(WebGLTexture
         return;
 
     if (!tex || tex->IsDeleted())
         return;
 
     if (mBoundFramebuffer)
         mBoundFramebuffer->DetachTexture(tex);
 
+    WebGLuint activeTexture = mActiveTexture;
     for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
         if ((tex->Target() == LOCAL_GL_TEXTURE_2D && mBound2DTextures[i] == tex) ||
             (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP && mBoundCubeMapTextures[i] == tex))
         {
             ActiveTexture(LOCAL_GL_TEXTURE0 + i);
             BindTexture(tex->Target(), static_cast<WebGLTexture*>(nullptr));
         }
     }
-    ActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
+    ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
 WebGLContext::DeleteProgram(WebGLProgram *prog)
 {
     if (!IsContextStable())
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/reftest/clip-multiple-paths-badref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="100" height="100"></canvas>
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 100);
+
+ctx.beginPath();
+ctx.arc(50, 50, 25, 0, 2 * Math.PI);
+ctx.clip();
+
+ctx.fillStyle = '#f00';
+
+ctx.fillRect(0, 0, 100, 100);
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/reftest/clip-multiple-paths.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="100" height="100"></canvas>
+
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 100);
+
+ctx.beginPath();
+ctx.rect(30, 30, 40, 40);
+ctx.clip();
+
+ctx.beginPath();
+ctx.arc(50, 50, 25, 0, 2 * Math.PI);
+ctx.clip();
+
+ctx.fillStyle = '#f00';
+
+ctx.fillRect(0, 0, 100, 100);
+
+</script>
+</body></html>
--- a/content/canvas/test/reftest/reftest.list
+++ b/content/canvas/test/reftest/reftest.list
@@ -181,8 +181,14 @@ pref(webgl.force-layers-readback,true) f
 pref(webgl.prefer-16bpp,true)                                                               == webgl-color-test.html?16bpp           wrapper.html?colors.png
 pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true) fails-if(nativeFennec) == webgl-color-test.html?16bpp&readback  wrapper.html?colors.png
 
 # Force native GL (Windows):
 skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-clear-test.html?native-gl        wrapper.html?green.png
 skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-orientation-test.html?native-gl  wrapper.html?white-top-left.png
 skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-color-test.html?native-gl        wrapper.html?colors.png
 skip-if(!winWidget) pref(webgl.prefer-native-gl,true) pref(webgl.prefer-16bpp,true)  == webgl-color-test.html?native-gl&16bpp  wrapper.html?colors.png
+
+# Do we correctly handle multiple clip paths?
+!= clip-multiple-paths.html clip-multiple-paths-badref.html
+
+# Bug 815648
+== stroketext-shadow.html stroketext-shadow-ref.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/reftest/stroketext-shadow-ref.html
@@ -0,0 +1,19 @@
+<!--docytpe html-->
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+<script>
+window.onload=function(){
+c=document.getElementById("myCanvas").getContext("2d");
+c.canvas.width=c.canvas.width;
+c.font="35px sans-serif";
+c.shadowColor="darkblue";
+c.shadowBlur=2;
+c.strokeText('ABCDEF',20,100);
+}
+</script>
+</head>
+<body>
+<canvas id="myCanvas" height="500" width="500" style="border:1px solid black"></canvas>
+
+</body></html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/reftest/stroketext-shadow.html
@@ -0,0 +1,20 @@
+<!--docytpe html-->
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+<script>
+window.onload=function(){
+c=document.getElementById("myCanvas").getContext("2d");
+c.canvas.width=c.canvas.width;
+c.font="35px sans-serif";
+c.shadowColor="darkblue";
+c.shadowBlur=2;
+c.moveTo(20,20);
+c.strokeText('ABCDEF',20,100);
+}
+</script>
+</head>
+<body>
+<canvas id="myCanvas" height="500" width="500" style="border:1px solid black"></canvas>
+
+</body></html>
--- a/content/canvas/test/webgl/skipped_tests_android.txt
+++ b/content/canvas/test/webgl/skipped_tests_android.txt
@@ -1,6 +1,32 @@
+conformance/glsl/functions/glsl-function-abs.html
+conformance/glsl/functions/glsl-function-ceil.html
+conformance/glsl/functions/glsl-function-clamp-float.html
+conformance/glsl/functions/glsl-function-clamp-gentype.html
+conformance/glsl/functions/glsl-function-cross.html
+conformance/glsl/functions/glsl-function-distance.html
+conformance/glsl/functions/glsl-function-dot.html
+conformance/glsl/functions/glsl-function-faceforward.html
+conformance/glsl/functions/glsl-function-floor.html
+conformance/glsl/functions/glsl-function-fract.html
 conformance/glsl/functions/glsl-function.html
+conformance/glsl/functions/glsl-function-length.html
+conformance/glsl/functions/glsl-function-max-float.html
+conformance/glsl/functions/glsl-function-max-gentype.html
+conformance/glsl/functions/glsl-function-min-float.html
+conformance/glsl/functions/glsl-function-min-gentype.html
+conformance/glsl/functions/glsl-function-mix-float.html
+conformance/glsl/functions/glsl-function-mix-gentype.html
+conformance/glsl/functions/glsl-function-mod-float.html
+conformance/glsl/functions/glsl-function-mod-gentype.html
+conformance/glsl/functions/glsl-function-normalize.html
+conformance/glsl/functions/glsl-function-reflect.html
+conformance/glsl/functions/glsl-function-sign.html
+conformance/glsl/functions/glsl-function-smoothstep-float.html
+conformance/glsl/functions/glsl-function-smoothstep-gentype.html
+conformance/glsl/functions/glsl-function-step-float.html
+conformance/glsl/functions/glsl-function-step-gentype.html
 conformance/more/conformance/quickCheckAPI-B2.html
 conformance/programs/gl-getshadersource.html
 conformance/reading/read-pixels-test.html
 conformance/textures/gl-teximage.html
 conformance/textures/tex-image-and-sub-image-2d-with-image.html
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -97,16 +97,19 @@
 #include "MediaPluginDecoder.h"
 #endif
 #ifdef MOZ_WIDGET_GONK
 #include "MediaOmxDecoder.h"
 #endif
 #ifdef MOZ_DASH
 #include "DASHDecoder.h"
 #endif
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gMediaElementLog;
 static PRLogModuleInfo* gMediaElementEventsLog;
 #define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
 #define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
 #else
 #define LOG(type, msg)
@@ -2158,17 +2161,16 @@ nsHTMLMediaElement::CreateDecoder(const 
   // bundled decoders, unless the "media.prefer-gstreamer" pref is set.
   if (DecoderTraits::IsGStreamerSupportedType(aType)) {
     nsRefPtr<GStreamerDecoder> decoder = new GStreamerDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
-
 #ifdef MOZ_RAW
   if (DecoderTraits::IsRawType(aType)) {
     nsRefPtr<RawDecoder> decoder = new RawDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
@@ -2207,25 +2209,32 @@ nsHTMLMediaElement::CreateDecoder(const 
 #ifdef MOZ_WEBM
   if (DecoderTraits::IsWebMType(aType)) {
     nsRefPtr<WebMDecoder> decoder = new WebMDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
-
 #ifdef MOZ_DASH
   if (DecoderTraits::IsDASHMPDType(aType)) {
     nsRefPtr<DASHDecoder> decoder = new DASHDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
+#ifdef MOZ_WMF
+  if (DecoderTraits::IsWMFSupportedType(aType)) {
+    nsRefPtr<WMFDecoder> decoder = new WMFDecoder();
+    if (decoder->Init(this)) {
+      return decoder.forget();
+    }
+  }
+#endif
 
   return nullptr;
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -8,16 +8,19 @@
 #include "MediaDecoder.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 #ifdef MOZ_GSTREAMER
 #include "mozilla/Preferences.h"
 #endif
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
 
 namespace mozilla
 {
 
 template <class String>
 static bool
 CodecListContains(char const *const * aCodecs, const String& aCodec)
 {
@@ -237,16 +240,23 @@ DecoderTraits::IsDASHMPDType(const nsACS
   if (!MediaDecoder::IsDASHEnabled()) {
     return false;
   }
 
   return CodecListContains(gDASHMPDTypes, aType);
 }
 #endif
 
+#ifdef MOZ_WMF
+bool DecoderTraits::IsWMFSupportedType(const nsACString& aType)
+{
+  return WMFDecoder::GetSupportedCodecs(aType, nullptr);
+}
+#endif
+
 /* static */
 bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
 {
 #ifdef MOZ_WAVE
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     // We should not return true for Wave types, since there are some
     // Wave codecs actually in use in the wild that we don't support, and
     // we should allow those to be handled by plugins or helper apps.
@@ -292,29 +302,33 @@ DecoderTraits::CanHandleMediaType(const 
 #endif
 #ifdef MOZ_DASH
   if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
     // DASH manifest uses WebM codecs only.
     codecList = gWebMCodecs;
     result = CANPLAY_YES;
   }
 #endif
-
 #ifdef MOZ_GSTREAMER
   if (IsH264Type(nsDependentCString(aMIMEType))) {
     codecList = gH264Codecs;
     result = CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_WIDGET_GONK
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
     codecList = gH264Codecs;
     result = CANPLAY_MAYBE;
   }
 #endif
+#ifdef MOZ_WMF
+  if (WMFDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList)) {
+    result = CANPLAY_MAYBE;
+  }
+#endif
 #ifdef MOZ_MEDIA_PLUGINS
   if (MediaDecoder::IsMediaPluginsEnabled() &&
       GetMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList))
     result = CANPLAY_MAYBE;
 #endif
   if (result == CANPLAY_NO || !aHaveRequestedCodecs) {
     return result;
   }
--- a/content/media/DecoderTraits.h
+++ b/content/media/DecoderTraits.h
@@ -64,14 +64,17 @@ public:
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsType(const nsACString& aType);
 #endif
 
 #ifdef MOZ_DASH
   static bool IsDASHMPDType(const nsACString& aType);
 #endif
 
+#ifdef MOZ_WMF
+  static bool IsWMFSupportedType(const nsACString& aType);
+#endif
 };
 
 }
 
 #endif
 
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -93,16 +93,20 @@ endif
 ifdef MOZ_DASH
 PARALLEL_DIRS += dash
 endif
 
 ifdef MOZ_MEDIA_PLUGINS
 PARALLEL_DIRS += plugins
 endif
 
+ifdef MOZ_WMF
+PARALLEL_DIRS += wmf
+endif
+
 PARALLEL_DIRS += webrtc
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 PARALLEL_DIRS += omx
 endif
 
 TEST_DIRS += test
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -18,16 +18,20 @@
 #include "nsContentUtils.h"
 #include "ImageContainer.h"
 #include "MediaResource.h"
 #include "nsError.h"
 #include "mozilla/Preferences.h"
 #include <cstdlib> // for std::abs(int/long)
 #include <cmath> // for std::abs(float/double)
 
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
+
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 // Number of milliseconds between progress events as defined by spec
 static const uint32_t PROGRESS_MS = 350;
 
@@ -1636,16 +1640,24 @@ MediaDecoder::IsMediaPluginsEnabled()
 #ifdef MOZ_DASH
 bool
 MediaDecoder::IsDASHEnabled()
 {
   return Preferences::GetBool("media.dash.enabled");
 }
 #endif
 
+#ifdef MOZ_WMF
+bool
+MediaDecoder::IsWMFEnabled()
+{
+  return WMFDecoder::IsEnabled();
+}
+#endif
+
 MediaMemoryReporter* MediaMemoryReporter::sUniqueInstance;
 
 NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedVideoMemory,
   "explicit/media/decoded-video",
   KIND_HEAP,
   UNITS_BYTES,
   MediaMemoryReporter::GetDecodedVideoMemory,
   "Memory used by decoded video frames.")
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -762,16 +762,20 @@ public:
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsEnabled();
 #endif
 
 #ifdef MOZ_DASH
   static bool IsDASHEnabled();
 #endif
 
+#ifdef MOZ_WMF
+  static bool IsWMFEnabled();
+#endif
+
   // Schedules the state machine to run one cycle on the shared state
   // machine thread. Main thread only.
   nsresult ScheduleStateMachineThread();
 
   struct Statistics {
     // Estimate of the current playback rate (bytes/second).
     double mPlaybackRate;
     // Estimate of the current download rate (bytes/second). This
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -409,16 +409,27 @@ public:
 
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
   // denote the start and end times of the media in usecs, and aCurrentTime
   // is the current playback position in microseconds.
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime) = 0;
+  
+  // Called when the decode thread is started, before calling any other
+  // decode, read metadata, or seek functions. Do any thread local setup
+  // in this function.
+  virtual void OnDecodeThreadStart() {}
+  
+  // Called when the decode thread is about to finish, after all calls to
+  // any other decode, read metadata, or seek functions. Any backend specific
+  // thread local tear down must be done in this function. Note that another
+  // decode thread could start up and run in future.
+  virtual void OnDecodeThreadFinish() {}
 
 protected:
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<AudioData> mAudioQueue;
 
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -462,39 +462,44 @@ int64_t MediaDecoderStateMachine::GetDec
     audioDecoded += mAudioEndTime - GetMediaTime();
   }
   return audioDecoded;
 }
 
 void MediaDecoderStateMachine::DecodeThreadRun()
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  mReader->OnDecodeThreadStart();
+  
+  {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-  if (mState == DECODER_STATE_DECODING_METADATA) {
-    if (NS_FAILED(DecodeMetadata())) {
+    if (mState == DECODER_STATE_DECODING_METADATA &&
+        NS_FAILED(DecodeMetadata())) {
       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
                    "Should be in shutdown state if metadata loading fails.");
       LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
     }
-  }
 
-  while (mState != DECODER_STATE_SHUTDOWN &&
-         mState != DECODER_STATE_COMPLETED &&
-         !mStopDecodeThread)
-  {
-    if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
-      DecodeLoop();
-    } else if (mState == DECODER_STATE_SEEKING) {
-      DecodeSeek();
+    while (mState != DECODER_STATE_SHUTDOWN &&
+           mState != DECODER_STATE_COMPLETED &&
+           !mStopDecodeThread)
+    {
+      if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
+        DecodeLoop();
+      } else if (mState == DECODER_STATE_SEEKING) {
+        DecodeSeek();
+      }
     }
+
+    mDecodeThreadIdle = true;
+    LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
   }
-
-  mDecodeThreadIdle = true;
-  LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
+  
+  mReader->OnDecodeThreadFinish();
 }
 
 void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
                                                DecodedStreamData* aStream,
                                                AudioSegment* aOutput)
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
--- a/content/media/nsDOMMediaStream.cpp
+++ b/content/media/nsDOMMediaStream.cpp
@@ -90,28 +90,30 @@ nsDOMLocalMediaStream::CreateSourceStrea
   nsRefPtr<nsDOMLocalMediaStream> stream = new nsDOMLocalMediaStream();
   stream->SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   stream->mStream = gm->CreateSourceStream(stream);
   return stream.forget();
 }
 
 already_AddRefed<nsDOMMediaStream>
-nsDOMMediaStream::CreateTrackUnionStream()
+nsDOMMediaStream::CreateTrackUnionStream(uint32_t aHintContents)
 {
   nsRefPtr<nsDOMMediaStream> stream = new nsDOMMediaStream();
+  stream->SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   stream->mStream = gm->CreateTrackUnionStream(stream);
   return stream.forget();
 }
 
 already_AddRefed<nsDOMLocalMediaStream>
-nsDOMLocalMediaStream::CreateTrackUnionStream()
+nsDOMLocalMediaStream::CreateTrackUnionStream(uint32_t aHintContents)
 {
   nsRefPtr<nsDOMLocalMediaStream> stream = new nsDOMLocalMediaStream();
+  stream->SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   stream->mStream = gm->CreateTrackUnionStream(stream);
   return stream.forget();
 }
 
 bool
 nsDOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
 {
--- a/content/media/nsDOMMediaStream.h
+++ b/content/media/nsDOMMediaStream.h
@@ -65,17 +65,17 @@ public:
     HINT_CONTENTS_VIDEO = 0x00000002U
   };
   uint32_t GetHintContents() const { return mHintContents; }
   void SetHintContents(uint32_t aHintContents) { mHintContents = aHintContents; }
 
   /**
    * Create an nsDOMMediaStream whose underlying stream is a TrackUnionStream.
    */
-  static already_AddRefed<nsDOMMediaStream> CreateTrackUnionStream();
+  static already_AddRefed<nsDOMMediaStream> CreateTrackUnionStream(uint32_t aHintContents = 0);
 
 protected:
   // MediaStream is owned by the graph, but we tell it when to die, and it won't
   // die until we let it.
   MediaStream* mStream;
   // Principal identifying who may access the contents of this stream.
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
@@ -101,12 +101,12 @@ public:
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream.
    */
   static already_AddRefed<nsDOMLocalMediaStream> CreateSourceStream(uint32_t aHintContents);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream.
    */
-  static already_AddRefed<nsDOMLocalMediaStream> CreateTrackUnionStream();
+  static already_AddRefed<nsDOMLocalMediaStream> CreateTrackUnionStream(uint32_t aHintContents = 0);
 };
 
 #endif /* NSDOMMEDIASTREAM_H_ */
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -416,24 +416,27 @@ nsresult OggReader::DecodeVorbis(ogg_pac
     }
   }
   return NS_OK;
 }
 #ifdef MOZ_OPUS
 nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
   NS_ASSERTION(aPacket->granulepos != -1, "Must know opus granulepos!");
 
-  // Maximum value is 63*2880.
+  // Maximum value is 63*2880, so there's no chance of overflow.
   int32_t frames_number = opus_packet_get_nb_frames(aPacket->packet,
                                                     aPacket->bytes);
+  if (frames_number <= 0)
+    return NS_ERROR_FAILURE; // Invalid packet header.
   int32_t samples = opus_packet_get_samples_per_frame(aPacket->packet,
                                                       (opus_int32) mOpusState->mRate);
   int32_t frames = frames_number*samples;
 
-  if (frames <= 0)
+  // A valid Opus packet must be between 2.5 and 120 ms long.
+  if (frames < 120 || frames > 5760)
     return NS_ERROR_FAILURE;
   uint32_t channels = mOpusState->mChannels;
   nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
 
   // Decode to the appropriate sample type.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   int ret = opus_multistream_decode_float(mOpusState->mDecoder,
                                           aPacket->packet, aPacket->bytes,
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -33,16 +33,17 @@ include $(DEPTH)/config/autoconf.mk
 # make the test first check canPlayType for the type, and if it's not
 # supported, just do ok(true, "Type not supported") and stop the test.
 
 MOCHITEST_FILES = \
 		allowed.sjs \
 		can_play_type_ogg.js \
 		can_play_type_wave.js \
 		can_play_type_webm.js \
+		can_play_type_mpeg.js \
 		cancellable_request.sjs \
 		dynamic_redirect.sjs \
 		dynamic_resource.sjs \
 		file_access_controls.html \
 		fragment_play.js \
 		fragment_noplay.js \
 		manifest.js \
 		reactivate_helper.html \
@@ -67,16 +68,17 @@ MOCHITEST_FILES = \
 		test_audio2.html \
 		test_autoplay.html \
 		test_bug465498.html \
 		test_bug493187.html \
 		test_bug495145.html \
 		test_bug495300.html \
 		test_bug686942.html \
 		test_can_play_type.html \
+		test_can_play_type_mpeg.html \
 		test_closing_connections.html \
 		test_constants.html \
 		test_controls.html \
 		test_currentTime.html \
 		test_decode_error.html \
 		test_defaultMuted.html \
 		test_delay_load.html \
 		test_error_on_404.html \
@@ -220,28 +222,30 @@ MOCHITEST_FILES += \
 		variable-samplerate.opus \
 		variable-channel.ogg \
 		variable-channel.opus \
 		chained-video.ogv \
 		chained-audio-video.ogg \
 		variable-preskip.opus \
 		dirac.ogg \
 		multiple-bos.ogg \
+		owl.mp3 \
 		split.webm \
 		seek.ogv \
 		seek.webm \
 		seek.yuv \
 		short-video.ogv \
 		small-shot.ogg \
+		small-shot.m4a \
+		small-shot.mp3 \
 		sound.ogg \
 		spacestorm-1000Hz-100ms.ogg \
 		video-overhang.ogg \
 		file_a4_tone.ogg \
 		detodos.opus \
-		short.mp4 \
 		notags.mp3 \
 		id3tags.mp3 \
 		$(NULL)
 
 # Wave sample files
 MOCHITEST_FILES += \
 		big.wav \
 		bogus.wav \
new file mode 100644
--- /dev/null
+++ b/content/media/test/can_play_type_mpeg.js
@@ -0,0 +1,27 @@
+function check_mp4(v, enabled) {
+  function check(type, expected) {
+    var ex = enabled ? expected : "";
+    is(v.canPlayType(type), ex, type + "='" + ex + "'");
+  }
+
+  check("video/mp4", "maybe");
+  check("audio/mp4", "maybe");
+  check("audio/mpeg", "maybe");
+
+  check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
+
+  check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.42001E\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001E\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001F\"", "probably");
+
+  check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
+  check("audio/mp4; codecs=mp4a.40.2", "probably");
+}
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -1,16 +1,18 @@
 // In each list of tests below, test file types that are not supported should
 // be ignored. To make sure tests respect that, we include a file of type
 // "bogus/duh" in each list.
 
 // These are small test files, good for just seeing if something loads. We
 // really only need one test file per backend here.
 var gSmallTests = [
   { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
+  { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
+  { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
   { name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
   { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
@@ -28,16 +30,17 @@ var gProgressTests = [
 
 // Used by test_played.html
 var gPlayedTests = [
   { name:"big.wav", type:"audio/x-wav", duration:9.0 },
   { name:"sound.ogg", type:"audio/ogg", duration:4.0 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
+  { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
 ];
 
 // Used by test_mozLoadFrom.  Need one test file per decoder backend, plus
 // anything for testing clone-specific bugs.
 var cloneKey = Math.floor(Math.random()*100000000);
 var gCloneTests = gSmallTests.concat([
   // Actual duration is ~200ms, we have Content-Duration lie about it.
   { name:"bug520908.ogv", type:"video/ogg", duration:9000 },
@@ -147,26 +150,30 @@ var gPlayTests = [
   // hardware.
   { name:"spacestorm-1000Hz-100ms.ogg", type:"audio/ogg", duration:0.099 },
 
   // Opus data in an ogg container
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
 
   { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
 
+  { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
+  { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
+  { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
+
   // Invalid file
   { name:"bogus.duh", type:"bogus/duh", duration:Number.NaN }
 ];
 
 // A file for each type we can support.
 var gSnifferTests = [
   { name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444 },
   { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942 },
   { name:"seek.webm", type:"video/webm", duration:3.966, size:215529 },
-  { name:"short.mp4", type:"video/mp4", duration:0.2, size:29435},
+  { name:"gizmo.mp4", type:"video/mp4", duration:5.56, size:383631 },
   // A mp3 file with id3 tags.
   { name:"id3tags.mp3", type:"audio/mpeg", duration:0.28, size:3530},
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // Files we must reject as invalid.
 var gInvalidTests = [
   { name:"invalid-m0c0.opus", type:"audio/ogg; codecs=opus"},
@@ -287,23 +294,27 @@ var gSeekTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"audio.wav", type:"audio/x-wav", duration:0.031247 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
   { name:"320x240.ogv", type:"video/ogg", duration:0.266 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"bug516323.indexed.ogv", type:"video/ogg", duration:4.208 },
   { name:"split.webm", type:"video/webm", duration:1.967 },
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
+  { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
+  { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
   { name:"bogus.duh", type:"bogus/duh", duration:123 }
 ];
 
 // These are files suitable for using with a "new Audio" constructor.
 var gAudioTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"sound.ogg", type:"audio/ogg" },
+  { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
+  { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
   { name:"bogus.duh", type:"bogus/duh", duration:123 }
 ];
 
 // These files ensure our handling of 404 errors is consistent across the
 // various backends.
 var g404Tests = [
   { name:"404.wav", type:"audio/x-wav" },
   { name:"404.ogv", type:"video/ogg" },
new file mode 100644
index 0000000000000000000000000000000000000000..9fafa32f93b2853972fc15aa954e4bface637feb
GIT binary patch
literal 67430
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
deleted file mode 100644
index 0b9870bdcb500389b78a84e7feb6cce31fd828b4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index 0000000000000000000000000000000000000000..51a23c5b490ac54af4eb7b9e66eacdc8c999d7ca
GIT binary patch
literal 2710
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
G+y4v4);q%h
new file mode 100644
index 0000000000000000000000000000000000000000..f9397a5106be1fe2b5838539096d43e5f0251f8d
GIT binary patch
literal 6825
zc%1E+c~BGSx`(@y5CU{U5)x=2VgfOOAa(#11jn$*W<*dJal<SQf+DhmbDSX|>;#5Q
zCLn4MWB_r+85Nx|Bw-PPqN1pyBPxPAE-)w~i{!!_oO9<^ojP@I-9PTFI!{%q`;T<x
z>%33@-nTzLFGmb83mm**!|ItM0RYfkep2{SCwm8bHi7`!NjdPZ2#t*1IdjmR`9J_*
zmIW-F!*z~;IRu|bnj>e9L!US^hkA~?pLjKgc8-6!sDJan%!~;;0085&guB#KgaQCO
zT@HZsfmX+hj{qV7I7w!{Lj<Jy9MQ}P{rFi7jy|B^IYh57ttP~WV+~xs|Mh9;f}HT(
zeW${H`sm^xlu>%|ZYD+np~WM#kAl`bW4lfMVZZ<QcGPhO|0Q7`RVvl&52IE8-4lqY
z;Ba6Hghc@&`s}iJEe=yE<hiCM`|61A<M<en0iBvpvw#lLx_OD+^S{hrVWc51ftF*`
zJa=h3cwXL*v+$Gv3bh`W?9Noa^V5Wg>eLVNxXV2tb>(p`k<+N;(4;7LjXJ^%f-_S(
zF64AGeH0zTA}RYu@3orGHH%y9R2oJff-~0q=qc~wx@cm36Iw2tNS~?!b-$BlNZEKZ
zjLNlYN9uHX2SFajS^wA-wh87Ix_=pRK(`4!{8$IODLr%rtPj?UW5_ZR=<EbaK@RGm
zR(pR)tG#mfcI0}OJx8?JZ?)QW>CH;XG5+@HORL`7E1OzV-<cRErBDIji_`ZS90cG|
zMh)8_i)>eAjp(oVoa+&Tr(MB@f{9sxnN7d52ydX^9SwB?#Zr|#9E`zZM9#k0LX<c?
zgCj^Qg!d!Z5=5M%&%xhD6Ldw|$)h`L31cU>-8iyKj+r%~9{|l)C$HG^?u25-gc$)a
zqnCzPST;h%AJB|kk)}To;Z<S_MUW-h02Jlmh#>d{-PEfY%hVSYaww)CcmbvbV0Fx~
zSb&4W8v!kJdGk?b@yvrhzW+dS(%y;k+Y@(N&FhmkHT$~WxPQIxo{|6bj#q7qTx#<U
zuTBn+N2lWoD=c5IW9~jK-FxPA;G=$=>;V;erS@{(;o&V~2?lD<S?_v|^+sI6=*;D4
zL4O7RUhXl|BKKGcoslNrlrfN#o38f_MqKptj8tx#`=ljZ)sioyZ(WJWtM$|OrqD7o
z&kgR<VXe~{`t@~Y^Ac~ScWszyo8%joiv{m_$JN-Wj-YeOGYK6dh3XA7g_7RVbD?t>
zRu)D;IQUDVjDSXGl;B%8fEYAcg`!((SN#!W{UV}Ymdlm*KQ!ASrn!t2btR2m+82q&
zyVh~?^7+rgNeL#F4C-io#<rj?a?IN)4**;~hFRYpz7Xf``e5oCaFI^|q0U3vexRvP
z4m+joS73a_8YV!eKsXeEeWSF3Ojs9Jcfq>y=4x)f;<j7<%LmarI=RqRxRWC(($L{6
zRcM_;oZ%v{RT`+LPw~3^a=aX)+sxf`q$BlH+)gT09NE{Fn$@v=+Leo1Ljp^Es`cFV
zxzC1zcftD;+j`B-JribC007W5W2f7mgg;eKq%Hhm=ZRHM65q%px*r?SKi7p-_!`Te
zmUmH?J3E%Hm}l$mk$tk^a?uq<cBT%$5Wws6%F3AwTAYILsU_K!%0q5P4I5O1yRSSQ
zh;C>TZ)bFM3n|Nk9&5(up?JU!96?<5SOmCaiP+!pA7?2eGg#urfsulN7spof4^4?5
z>gP1tm6w4wSy|(S>Ec|SePXp=X@C6z(c|1|zaqs*HqS*W2$C`w62(Pvb&wdeE^5|8
zbBSn8kRB!@UQfLA%HOX1@jnbxIE~iZwZHBjA~adLV6w=Uu4o^)ujDShXOpnBu*SZ*
zlB%W=T&Fh!z_HU*_u4aG6sl?TB2g<>&8-DG7N32Ck;NDAh6RNTB&99Q3JY|_3F!cj
zr{tvxdg_2S#&E1D2}*JS9iW%a2%x#M(kia4pfzEsxz`iL&H4-KmJRp}uKg;6L+{9p
z*GcCRSxBgL=dq`{A`O9v)D5u7B9%Fo3*ngrKn!r4j(jSic32@#3fQyce(e6Qu5VPI
z&DJqD@!3TD_T%l>@kUw~=<>_<hmKJ~P6p*!B1N(eamUtrr`1}=40)KIShHD=NIOq{
z#gUVahJ!%HwWUJ0I!C;5=)+3yk$f~jtmDsyLQ26{^tK!3Zhx9!QkCo#HT=3~&V)*(
zq&Pi7egO(yJY2`Tx`TRmkr9Pqio`{TFVM4h4w+US+?o5EdCS-yL1e}?tR=JJw363f
zl#jwa=eslV+U+7bJ<v|ZzFr*Ob66=55v@hAn=Zh`vRO7*T=bl+Y6W4TNoAt8!FIx`
zn|~wax63!0D{J3;OdJh*Q2yX?8*=!K5Z;GnZ?DkAl=*G$+{R6<oq4FnlfFG;yXHJD
zCS+t^hBU0$c3F}WH6-)HS)sy1@kOsO5rYOrV!+zct4Y_`N8W!F-DFB)qpg<z%^9nj
zp6{Sm(a4VM8_JM0vJs9%0y7CF&r^ciL5LYlN8}V3g-|*?suuhlYU254O{SiC1YraB
zU%R3m+L744@_S3qb+mw9Teqt_5{KX1o9b<g9l4Ub;Qp_hUkeQ@JgN0%C&!Fhh2w2U
zg3KZZ?zL`wbbZ9psPf3v&HDyPn~M*)9aD+@DaG~%EA6ltDBi+C*Z0}fiJ7t!@xYR!
zyKRKUSda#IR~5wo<046U1=y{S?JL3+VnV&m=y(c6Q>vG|P}D<?m!CCUtb2lMKKRr2
zJA|%%bE^$v>O<dpaw9cy4aXceEwx^bsv(gxb95Koh`)F6lxN%ZLAFP@%hdbXP^c|<
z|2$$Q6t;X)fwC_-I4bH-6_7!vr+p#ThF@sv8{HOU%(qDn%$zC0hq48}Pp0-%cy9fj
z%+XN~?8?A^@*yfZ)*!+dC^%ch(=jvC<3BsKNG#3CmDm*m91v5r^{ysBMrp%qF6>X~
z?L?QO4*R5aV?Ywn$cqs^Fb2&GAY#CRib`G;Zl2`E+|9J3QT<PJ%U{1}j(W}3^|KHh
z%{Qn|oqRoX++t63^|j?I+kJHN51eTT4+$=`Cq2l$=AlbmzG|%#mC>MoqhUd0Q^#{N
zFGs2wrZRkSU1mfT>w6z>z2n}yvuew&!>hY9cRDYOEUTr)2r|JA*d2HUb49!0eLRZH
zgW&|H^D<$K!cs;BDq*U~Rffoy!LYj?tgixKC*DPv21;a`-Rof=c_O^S-Ox1VY5GA2
zRGgt(icpXt7c}NlcoeZ2KS1{m&stK)Ye*mFm4O|+mpXO=owNdeU|Is-4)}q;T~fpU
z7Etno(!S>BNIH0WAdQy+RPw^p26?Gz%XlqdBX2!gecU1vKb?Hxt@dZ_S_Y`>^u$VT
zdL>Zs-0gk8ucx{Ou?ng$-a<cbTGVwSbwX-1S>YI6QR2QV>4fjuV&B7zHU0@vYhk+l
zwZX1YhGsL@eeLRo1^{3+0#UP}@PhCj*>>A>()E)GR^$6D?Wggd+JhVInbC5yp!%G$
z3Xd19M=D~#bN6T{(@Ui6V}|b|x=Oh&I^0th&;=rN%0_BxD-C0E=|)m`<*k_OBB_@b
zY`ogcB3UGhhx?xTlAf;3Xe!=nM4Upet!?Tt6q>Cr`D~rg8XNCSzD4vk`mDgwNTL!d
zM~y^Aggj4D=LUUGM;$KOLSjVCjyZFO|KS&8q3ke5W`~2b>5ZEAe|Lo<(Id^Dzy1C6
zw42w#Q5^nYbLvn+(mUbB%sm_6U&F3F<)?6zEsv&(e53SC%T(>55pYKUzl3xk05BF`
z_G5i-H?RMvIQ-*Fy9PBs!$(=+)<HP{MfOe{n;XL{|65*Fj`bVa;cxiqiX7O6kdqdf
z1pDjt-0(N`*Ec=njF)|08GFEw183>-MKAYOly1za89IFE%yICnd#!A$;7oqidN?E}
zr^K07F3+*awqcplvaaTKtdaeaF)pdvZ%E@yLYJ2FGRME+#LL!a^3ynDus3ETn_dp@
z!P^uR5oCz?Hv>g!@+HmeOtOV#g;?lzE;DE~aw=(lzu4^N7WuhtMQpZvvEQ9=$(Fv!
z>1!w7oGb2a_$p*k=dJ~5FA`sWwteUQN54#9Z_ifW(cZ<`9w11TIcw6lfaZ`_F!KDt
zY$%KsjOB0YKKPYu%$y2zb}}pA%Rg1nc&Ok!ZkdlWy|h=qX>~P(#V&8eJiKePFju;z
zMWjeKle)NDD*_?N&Lg6YiQ<~H(p4X@Qg@(3Ic3eY%#gRdgQRX~TzeszyNc7(UPIB?
zse9YXZ8cS8=N?|RUb_U#Q3rA%0_vS2bThyS=BH3twt_eT)5e?jc!ki}WzeAi4j;>?
zwE%DO$c8LKa1j&Agc#BMpKzH(h7-Tm$lFBB6~Mr6ovsguX62Y*piE+4x?~N^|G402
z=a2S-I#2g*ex%j@y8Ygoq)D4~8^>%uJ5!rP%k(3%3=fCHsM^IA9fpn%d>m~;mH~ui
zjRB6-(@0U^a`mvIwFX%=fcWXEuq}pcOR^Ovu8;i=CBZ5P4rk4qu*hc!Pp{e#As|6i
zPxJn_><JSIkr(V>>?>q*Jdk`Fqt8gcJHPyW)Q{T54#ZZInU0QlT8UeW2>EFhnrJCQ
z`mEy3-5;_C7{qowI|Z*I5MQzVtV&>h15#lck0mPEUX*IHDh3in@Fc|SN1{k*-Qo2C
zIct}>d_6gHvqPfbrGRWbUhQrqJ2gGk9kJx&w_5GXBj?vS`mDLEil*1vKHVu@?fdzp
zQ^TJsI5qr**-)4y{JvpRm(k(fpG>gWl*lBEul;1gUoHRu<C<G#(7fPXg%K&30n1HJ
zl<FlS_WE1Y-OB^%N$z4n_VX#BqCwDclCWLsqyuf3C(T$S4YSt}sZgV(W)qbgWTII~
zmoBo9lF8j39E};NV4q9=Wyp}D_$EIspWZ5+XUgqb!ZqLQ6@IalGm?(xXDIfiS@I8X
zA~Of?nhs=Q%hBMaJYFQ-86HW%F0%ylOL+VuU-)ATRy+@VkRHx(u0EVJGl5K%E-plc
zpVDf_majE!)sG0C{-ATq*Q}}!>0_EX_+D+2UmxjhBXL#qsO}<q>$ApI4Ac_$O8G89
zz5ZM)x$@fM4uvGRKLk=8=xI-}<tDaADF6@dVrBN#1QRI(YkhUAzcgE?U-4=?L{as&
zHGU3}d^n1ZdhQDEm<~-%_bz?R?TBb7I#2y*?{O$JbE<u{u4b(-@oMNsXw*}El@=$p
zLiMOB%b<{sP_^BBu5Wr_m&leoA4_Tfz`B}sxy8#wL(`Efc?>BP!&Fitppcd+bw5`F
zp|2j;J%8x!cCB_gyG9ahfRkj`*Qc}?PJZ=1VBu!lOR3uFM;oH=Pi*;-+K}eur(YsO
zll`=Z{t*gJA%A5y6kZd?!anOZ|Ei(?lM2TYEeR8UhQePi0Q4od_rHAR(l0HQI3c}1
zFUNa?!>l&X*ze@YG?|a&f;CGQOla|<YUu9V793M*BRZ`JBtkePq!`eHoYH)=g%Ck$
zr~2UN?h(K{YmPO4Lf|n?2A8JQ31Zd^ZsYpz*i39&;C^{WG(Fs){~Ujs)P}XR=O>{C
zL5_xxC1#>IFL`!AxL%qQcGOi|cz_p)SYs}8?zWwFIGO-K3$Rsz>_`Bz%tT{J9E~a#
z&&=O8Gw@9(q?KNGJ#}T{+p6B@=59|?CkOJDaYu}8FWY@_^N!Hwa5%d`Ae$D{B?vp5
zg*iV6*+&JrKVY{6kU(juOZ%Fp^m(cUc8=f^f5bz_h^X*lZ-YlkEpp@~&C29VIAW@I
z3nrPds|i+E<U96ntPK?Q9=3RK6J>p~<2x(QHQ~+<EAf)oRBy+v^He@$nu7(?$~x8x
zF|z1s`5x&r5L(8?BCU8i-y8<gO>^kGWvhT3v5W=}h{rP&cV%W^P;TKyI4aAf5aGhx
z&^8j0wai<LTD9@Vy@qdRXW4XAmMCyB0e?-aeJ@~!e5ciZoH+0Q^@&x{Pj5tSeP_Ay
z$iyPcf2aW9(P8G&ln|tjrsO@H4TV<)W8b-HjKUfQ=1iC;PNe^*P;f1VzIJeGNw_{(
zf@4FLsZKlIsjrEcqV77O|4&l$bS{8nk*Z4hzgP{@`3ikgm2UqVq|{8M*YATA8(Zo0
z2#|-ACT7ffc8xLGh$mxnNVkGmET{dB=;RF_*yFejEXOe92hu2BJjyP$*nr~oBYnoV
z0G`b}P>AYxXXirkx{5$#f^3EFS%MUytI^2MNH|zRmjA-`Ab9GYR_mkc#VP)<p{QDG
zcRnj-wm*DONwA`5?fb62YgK*8TmJr%a&mLQ9qfxWmK7$HAA2Sq_%?QAKTOYCmN(i}
zALSZJkTf20^h#@#kSPh|M-&>(SuJCuis8PJmEWU+sXC^>nqf715n+c8=!ZGpTM+C(
z0gj({=YD?kyU>>r5=eY2{r6!NA@x@XQH`n~m+_%T55Jt@Z(r7AK6^K*EG}++&c7RH
zrrMgkpw#}pT0nRv+tU^Ic&h6B9?tC*?Yl=(p1k6$SbcUP{PgKOlDdkYTeRNJoBk#&
z=k(Zxfi?E3y_4D{b=r&3lUnodIt6gtqw&c?)raYTjj6P(k*TuK2Pc=k@hoFKK0iJG
ze9+Hvk3M|8{eg`YaVq+wFMle_&i`SS<v(i=4YQ#zQ7|^P*{|fVZQPs+^Q)@;6Z8Mr
zXiO9Rm;Ug79{?Z`F#ymrLSmgrJu(&few$W&a{simHa9JN36vz;J1#vwa+GxSepxeq
zq2o-O1&nL$DTBdymVRkhq_W8Aly;Pu;fV4P+W2Q6NDY!uGj(FdBn?_$GtU45(OkMt
zi>N%Eqj9u3)HObnHgqz?B6zegGW#Ek89`YLdVezcz`Bq}owF@Ub;CJk4Lg*9WN&1P
zToEV&IlUCfZexl=qMn^PvE{<7q2po(DP0~&UyRUj7_^`zM-1voL@<U00(7D<b)&@>
zr@HL_aCw3);qd9O9p()EG`0{>P;nk|t6VBzwABn`-f^8u)_gzF7yD?{+$*m&s1%Fz
zd1zoMj1|q-=8694pzwzY2SvD-yVh6;<-n{7irGD0u!aTz^w}-v|9>X@oBt920VCB`
ABLDyZ
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_can_play_type_mpeg.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=799315
+-->
+<head>
+  <title>Test for MP4 and MP3 support</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<video id="v"></video>
+
+<pre id="test">
+<script src="can_play_type_mpeg.js"></script>
+<script>
+
+function IsWindowsVistaOrLater() {
+  var re = /Windows NT (\d.\d)/;
+  var winver = navigator.userAgent.match(re);
+  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
+}
+
+function getMediaPref(name) {
+  // Can't use SpecialPowers.getBoolPref because it throws when pref isn't
+  // present, and for example on non-Windows systems the WMF prefs won't be
+  // present.
+  var pref = false;
+  var prefService = SpecialPowers.wrap(SpecialPowers.Components)
+                                 .classes["@mozilla.org/preferences-service;1"]
+                                 .getService(SpecialPowers.Ci.nsIPrefService);
+  var branch = prefService.getBranch("media.");
+  try {
+    pref = branch.getBoolPref(name);
+  } catch(ex) { }
+  return pref;
+}
+
+var haveMp4 = (getMediaPref("windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
+               getMediaPref("omx.enabled") ||
+               getMediaPref("gstreamer.enabled");
+// TODO:  Add "getMediaPref("plugins.enabled")" once MP4 works on Gingerbread.
+             
+check_mp4(document.getElementById('v'), haveMp4);
+
+mediaTestCleanup();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/Makefile.in
@@ -0,0 +1,30 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH		= @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+FAIL_ON_WARNINGS := 1
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= content
+LIBRARY_NAME	= gkconwmf_s
+LIBXUL_LIBRARY 	= 1
+
+EXPORTS		+= \
+		WMFDecoder.h \
+		$(NULL)
+
+CPPSRCS		= \
+		WMFByteStream.cpp \
+		WMFDecoder.cpp \
+		WMFReader.cpp \
+		WMFUtils.cpp \
+		$(NULL)
+
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMF.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WMF_H_
+#define WMF_H_
+
+#if WINVER < _WIN32_WINNT_WIN7
+#error \
+You must include WMF.h before including mozilla headers, \
+otherwise mozconfig.h will be included \
+and that sets WINVER to WinXP, \
+which makes Windows Media Foundation unavailable.
+#endif
+
+#pragma push_macro("WINVER")
+#undef WINVER
+#define WINVER _WIN32_WINNT_WIN7
+
+#include <windows.h>
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mfreadwrite.h>
+#include <mfobjects.h>
+#include <stdio.h>
+#include <mferror.h>
+#include <comdef.h>
+#include <propvarutil.h>
+#include <wmcodecdsp.h>
+
+#pragma comment(lib,"uuid.lib")
+#pragma comment(lib,"mfuuid.lib")
+
+_COM_SMARTPTR_TYPEDEF(IMFSourceReader, IID_IMFSourceReader);
+_COM_SMARTPTR_TYPEDEF(IMFMediaType, IID_IMFMediaType);
+_COM_SMARTPTR_TYPEDEF(IMFSample, IID_IMFSample);
+_COM_SMARTPTR_TYPEDEF(IMFMediaBuffer, IID_IMFMediaBuffer);
+_COM_SMARTPTR_TYPEDEF(IMFAsyncResult, IID_IMFAsyncResult);
+_COM_SMARTPTR_TYPEDEF(IMF2DBuffer, IID_IMF2DBuffer);
+
+namespace mozilla {
+namespace wmf {
+
+// Loads/Unloads all the DLLs in which the WMF functions are located.
+// The DLLs must be loaded before any of the WMF functions below will work.
+// All the function definitions below are wrappers which locate the
+// corresponding WMF function in the appropriate DLL (hence why LoadDLL()
+// must be called first...).
+HRESULT LoadDLLs();
+HRESULT UnloadDLLs();
+
+// All functions below are wrappers around the corresponding WMF function,
+// and automatically locate and call the corresponding function in the WMF DLLs.
+
+HRESULT MFStartup();
+
+HRESULT MFShutdown();
+
+HRESULT MFPutWorkItem(DWORD aWorkQueueId,
+                      IMFAsyncCallback *aCallback,
+                      IUnknown *aState);
+
+HRESULT MFAllocateWorkQueue(DWORD *aOutWorkQueueId);
+
+HRESULT MFUnlockWorkQueue(DWORD aWorkQueueId);
+
+HRESULT MFCreateAsyncResult(IUnknown *aUunkObject,
+                            IMFAsyncCallback *aCallback,
+                            IUnknown *aUnkState,
+                            IMFAsyncResult **aOutAsyncResult);
+
+HRESULT MFInvokeCallback(IMFAsyncResult *aAsyncResult);
+
+HRESULT MFCreateMediaType(IMFMediaType **aOutMFType);
+
+HRESULT MFCreateSourceReaderFromByteStream(IMFByteStream *aByteStream,
+                                           IMFAttributes *aAttributes,
+                                           IMFSourceReader **aOutSourceReader);
+
+HRESULT PropVariantToUInt32(REFPROPVARIANT aPropvar, ULONG *aOutUL);
+
+HRESULT PropVariantToInt64(REFPROPVARIANT aPropVar, LONGLONG *aOutLL);
+
+HRESULT MFTGetInfo(CLSID aClsidMFT,
+                   LPWSTR *aOutName,
+                   MFT_REGISTER_TYPE_INFO **aOutInputTypes,
+                   UINT32 *aOutNumInputTypes,
+                   MFT_REGISTER_TYPE_INFO **aOutOutputTypes,
+                   UINT32 *aOutNumOutputTypes,
+                   IMFAttributes **aOutAttributes);
+
+HRESULT MFGetStrideForBitmapInfoHeader(DWORD aFormat,
+                                       DWORD aWidth,
+                                       LONG *aOutStride);
+
+// Note: We shouldn't use this in production code; it's really only
+// here so we can compare behaviour of the SourceReader using WMF's
+// byte stream and ours when debugging.
+HRESULT MFCreateSourceReaderFromURL(LPCWSTR aURL,
+                                    IMFAttributes *aAttributes,
+                                    IMFSourceReader **aSourceReader);
+
+} // end namespace wmf
+} // end namespace mozilla
+
+
+
+#pragma pop_macro("WINVER")
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFByteStream.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+
+#include "Unknwn.h"
+#include <ole2.h>
+
+#include "WMFByteStream.h"
+#include "WMFUtils.h"
+#include "MediaResource.h"
+#include "nsISeekableStream.h"
+
+namespace mozilla {
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* gWMFByteStreamLog = nullptr;
+#define LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+HRESULT
+DoGetInterface(IUnknown* aUnknown, void** aInterface)
+{
+  if (!aInterface)
+    return E_POINTER;
+  *aInterface = aUnknown;
+  aUnknown->AddRef();
+  return S_OK;
+}
+
+WMFByteStream::WMFByteStream(MediaResource* aResource)
+  : mWorkQueueId(MFASYNC_CALLBACK_QUEUE_UNDEFINED),
+    mReentrantMonitor("WMFByteStream"),
+    mOffset(0),
+    mResource(aResource)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  NS_ASSERTION(mResource, "Must have a valid media resource");
+
+#ifdef PR_LOGGING
+  if (!gWMFByteStreamLog) {
+    gWMFByteStreamLog = PR_NewLogModule("WMFByteStream");
+  }
+#endif
+
+  MOZ_COUNT_CTOR(WMFByteStream);
+}
+
+WMFByteStream::~WMFByteStream()
+{
+  MOZ_COUNT_DTOR(WMFByteStream);
+}
+
+nsresult
+WMFByteStream::Init()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  // Work queue is not yet initialized, try to create.
+  HRESULT hr = wmf::MFAllocateWorkQueue(&mWorkQueueId);
+  if (FAILED(hr)) {
+    NS_WARNING("WMFByteStream Failed to allocate work queue.");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+nsresult
+WMFByteStream::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  if (mWorkQueueId != MFASYNC_CALLBACK_QUEUE_UNDEFINED) {
+    HRESULT hr = wmf::MFUnlockWorkQueue(mWorkQueueId);
+    if (FAILED(hr)) {
+      NS_WARNING("WMFByteStream Failed to unlock work queue.");
+      LOG("WMFByteStream unlock work queue hr=%x %d\n", hr, hr);
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
+
+// IUnknown Methods
+STDMETHODIMP
+WMFByteStream::QueryInterface(REFIID aIId, void **aInterface)
+{
+  LOG("WMFByteStream::QueryInterface %s", GetGUIDName(aIId).get());
+
+  if (aIId == IID_IMFByteStream) {
+    return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
+  }
+  if (aIId == IID_IMFAsyncCallback) {
+    return DoGetInterface(static_cast<IMFAsyncCallback*>(this), aInterface);
+  }
+  if (aIId == IID_IUnknown) {
+    return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
+  }
+
+  *aInterface = NULL;
+  return E_NOINTERFACE;
+}
+
+NS_IMPL_THREADSAFE_ADDREF(WMFByteStream);
+NS_IMPL_THREADSAFE_RELEASE(WMFByteStream);
+
+NS_IMPL_THREADSAFE_ADDREF(WMFByteStream::AsyncReadRequestState);
+NS_IMPL_THREADSAFE_RELEASE(WMFByteStream::AsyncReadRequestState);
+
+// IUnknown Methods
+STDMETHODIMP
+WMFByteStream::AsyncReadRequestState::QueryInterface(REFIID aIId, void **aInterface)
+{
+  LOG("WMFByteStream::AsyncReadRequestState::QueryInterface %s", GetGUIDName(aIId).get());
+
+  if (aIId == IID_IUnknown) {
+    return DoGetInterface(static_cast<IUnknown*>(this), aInterface);
+  }
+
+  *aInterface = NULL;
+  return E_NOINTERFACE;
+}
+
+// IMFByteStream Methods
+STDMETHODIMP
+WMFByteStream::BeginRead(BYTE *aBuffer,
+                         ULONG aLength,
+                         IMFAsyncCallback *aCallback,
+                         IUnknown *aCallerState)
+{
+  NS_ENSURE_TRUE(aBuffer, E_POINTER);
+  NS_ENSURE_TRUE(aCallback, E_POINTER);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  LOG("WMFByteStream::BeginRead() mOffset=%lld tell=%lld length=%lu",
+      mOffset, mResource->Tell(), aLength);
+
+  // Create an object to store our state.
+  IUnknownPtr requestState = new AsyncReadRequestState(mOffset, aBuffer, aLength);
+
+  // Create an IMFAsyncResult, this is passed back to the caller as a token to
+  // retrieve the number of bytes read.
+  IMFAsyncResultPtr callersResult;
+  HRESULT hr = wmf::MFCreateAsyncResult(requestState,
+                                        aCallback,
+                                        aCallerState,
+                                        &callersResult);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Queue a work item on our Windows Media Foundation work queue to call
+  // this object's Invoke() function which performs the read, and in turn
+  // invokes the caller's callback to notify the caller that the read
+  // operation is complete. Note: This creates another IMFAsyncResult to
+  // wrap callersResult, and it's that wrapping IMFAsyncResult which is
+  // passed to Invoke().
+  hr = wmf::MFPutWorkItem(mWorkQueueId, this, callersResult);
+
+  return hr;
+}
+
+// IMFAsyncCallback::Invoke implementation. This is called back on the work
+// queue thread, and performs the actual read.
+STDMETHODIMP
+WMFByteStream::Invoke(IMFAsyncResult* aResult)
+{
+  // Note: We assume that the WMF Work Queue that calls this function does
+  // so synchronously, i.e. this function call returns before any other
+  // work queue item runs. This is important, as if we run multiple instances
+  // of this function at once we'll interleave seeks and reads on the
+  // media resoure.
+
+  // Extract the caller's IMFAsyncResult object from the wrapping aResult object.
+  IMFAsyncResultPtr callerResult;
+  IUnknownPtr unknown;
+  HRESULT hr = aResult->GetState(&unknown);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  callerResult = unknown;
+  NS_ENSURE_TRUE(callerResult, E_FAIL);
+
+  // Get the object that holds our state information for the asynchronous call.
+  hr = callerResult->GetObject(&unknown);
+  NS_ENSURE_TRUE(SUCCEEDED(hr) && unknown, hr);
+  AsyncReadRequestState* requestState =
+    static_cast<AsyncReadRequestState*>(unknown.GetInterfacePtr());
+
+  // Ensure the read head is at the correct offset in the resource. It may not
+  // be if the SourceReader seeked.
+  if (mResource->Tell() != requestState->mOffset) {
+    nsresult rv = mResource->Seek(nsISeekableStream::NS_SEEK_SET,
+                                  requestState->mOffset);
+    if (NS_FAILED(rv)) {
+      // Let SourceReader know the read failed.
+      callerResult->SetStatus(E_FAIL);
+      wmf::MFInvokeCallback(callerResult);
+      LOG("WMFByteStream::Invoke() seek to read offset failed, aborting read");
+      return S_OK;
+    }
+  }
+  NS_ASSERTION(mResource->Tell() == requestState->mOffset, "State mismatch!");
+
+  // Read in a loop to ensure we fill the buffer, when possible.
+  ULONG totalBytesRead = 0;
+  nsresult rv = NS_OK;
+  while (totalBytesRead < requestState->mBufferLength) {
+    BYTE* buffer = requestState->mBuffer + totalBytesRead;
+    ULONG bytesRead = 0;
+    ULONG length = requestState->mBufferLength - totalBytesRead;
+    rv = mResource->Read(reinterpret_cast<char*>(buffer),
+                         length,
+                         reinterpret_cast<uint32_t*>(&bytesRead));
+    totalBytesRead += bytesRead;
+    if (NS_FAILED(rv) || bytesRead == 0) {
+      break;
+    }
+  }
+
+  // Record the number of bytes read, so the caller can retrieve
+  // it later.
+  requestState->mBytesRead = NS_SUCCEEDED(rv) ? totalBytesRead : 0;
+  callerResult->SetStatus(S_OK);
+
+  LOG("WMFByteStream::Invoke() read %d at %lld finished rv=%x",
+       requestState->mBytesRead, requestState->mOffset, rv);
+
+  // Let caller know read is complete.
+  wmf::MFInvokeCallback(callerResult);
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::BeginWrite(const BYTE *, ULONG ,
+                          IMFAsyncCallback *,
+                          IUnknown *)
+{
+  LOG("WMFByteStream::BeginWrite()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Close()
+{
+  LOG("WMFByteStream::Close()");
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead)
+{
+  NS_ENSURE_TRUE(aResult, E_POINTER);
+  NS_ENSURE_TRUE(aBytesRead, E_POINTER);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  // Extract our state object.
+  IUnknownPtr unknown;
+  HRESULT hr = aResult->GetObject(&unknown);
+  if (FAILED(hr) || !unknown) {
+    return E_INVALIDARG;
+  }
+  AsyncReadRequestState* requestState =
+    static_cast<AsyncReadRequestState*>(unknown.GetInterfacePtr());
+
+  // Important: Only advance the read cursor if the caller hasn't seeked
+  // since it called BeginRead(). If it has seeked, we still must report
+  // the number of bytes read (in *aBytesRead), but we don't advance the
+  // read cursor; reads performed after the seek will do that. The SourceReader
+  // caller seems to keep track of which async read requested which range
+  // to be read, and does call SetCurrentPosition() before it calls EndRead().
+  if (mOffset == requestState->mOffset) {
+    mOffset += requestState->mBytesRead;
+  }
+
+  // Report result.
+  *aBytesRead = requestState->mBytesRead;
+
+  LOG("WMFByteStream::EndRead() offset=%lld *aBytesRead=%u mOffset=%lld hr=0x%x eof=%d",
+      requestState->mOffset, *aBytesRead, mOffset, hr, (mOffset == mResource->GetLength()));
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::EndWrite(IMFAsyncResult *, ULONG *)
+{
+  LOG("WMFByteStream::EndWrite()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Flush()
+{
+  LOG("WMFByteStream::Flush()");
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetCapabilities(DWORD *aCapabilities)
+{
+  LOG("WMFByteStream::GetCapabilities()");
+  NS_ENSURE_TRUE(aCapabilities, E_POINTER);
+  *aCapabilities = MFBYTESTREAM_IS_READABLE |
+                   MFBYTESTREAM_IS_SEEKABLE;
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetCurrentPosition(QWORD *aPosition)
+{
+  NS_ENSURE_TRUE(aPosition, E_POINTER);
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  *aPosition = mOffset;
+  LOG("WMFByteStream::GetCurrentPosition() %lld", mOffset);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetLength(QWORD *aLength)
+{
+  NS_ENSURE_TRUE(aLength, E_POINTER);
+  int64_t length = mResource->GetLength();
+  *aLength = length;
+  LOG("WMFByteStream::GetLength() %lld", length);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::IsEndOfStream(BOOL *aEndOfStream)
+{
+  NS_ENSURE_TRUE(aEndOfStream, E_POINTER);
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  *aEndOfStream = (mOffset == mResource->GetLength());
+  LOG("WMFByteStream::IsEndOfStream() %d", *aEndOfStream);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::Read(BYTE*, ULONG, ULONG*)
+{
+  LOG("WMFByteStream::Read()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
+                    LONGLONG aSeekOffset,
+                    DWORD aSeekFlags,
+                    QWORD *aCurrentPosition)
+{
+  LOG("WMFByteStream::Seek(%d, %lld)", aSeekOrigin, aSeekOffset);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  if (aSeekOrigin == msoBegin) {
+    mOffset = aSeekOffset;
+  } else {
+    mOffset += aSeekOffset;
+  }
+
+  if (aCurrentPosition) {
+    *aCurrentPosition = mOffset;
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::SetCurrentPosition(QWORD aPosition)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  LOG("WMFByteStream::SetCurrentPosition(%lld)",
+      aPosition);
+
+  // Note: WMF calls SetCurrentPosition() sometimes after calling BeginRead()
+  // but before the read has finished, and thus before it's called EndRead().
+  // See comment in EndRead() for more details.
+
+  int64_t length = mResource->GetLength();
+  if (length >= 0 && aPosition > uint64_t(length)) {
+    // Despite the fact that the MSDN IMFByteStream::SetCurrentPosition()
+    // documentation says that we should return E_INVALIDARG when the seek
+    // position is after the length, if we do that IMFSourceReader::ReadSample()
+    // fails in some situations. So we just clamp the seek target to
+    // the EOS, and things seem to just work...
+    LOG("WMFByteStream::SetCurrentPosition(%lld) clamping position to eos (%lld)", aPosition, length);
+    aPosition = length;
+  }
+  mOffset = aPosition;
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::SetLength(QWORD)
+{
+  LOG("WMFByteStream::SetLength()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Write(const BYTE *, ULONG, ULONG *)
+{
+  LOG("WMFByteStream::Write()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::GetParameters(DWORD*, DWORD*)
+{
+  LOG("WMFByteStream::GetParameters()");
+  return E_NOTIMPL;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFByteStream.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#if !defined(WMFByteStream_h_)
+#define WMFByteStream_h_
+
+#include "WMF.h"
+
+#include "nsISupportsImpl.h"
+#include "nsCOMPtr.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+class MediaResource;
+
+// Wraps a MediaResource around an IMFByteStream interface, so that it can
+// be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
+// on which blocking I/O is performed. The SourceReader requests reads
+// asynchronously using {Begin,End}Read(). The synchronous I/O methods aren't
+// used by the SourceReader, so they're not implemented on this class.
+class WMFByteStream : public IMFByteStream,
+                      public IMFAsyncCallback
+{
+public:
+  WMFByteStream(MediaResource* aResource);
+  ~WMFByteStream();
+
+  nsresult Init();
+  nsresult Shutdown();
+
+  // IUnknown Methods.
+  STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
+  STDMETHODIMP_(ULONG) AddRef();
+  STDMETHODIMP_(ULONG) Release();
+
+  // IMFByteStream Methods.
+  STDMETHODIMP BeginRead(BYTE *aBuffer,
+                         ULONG aLength,
+                         IMFAsyncCallback *aCallback,
+                         IUnknown *aCallerState);
+  STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
+                          IMFAsyncCallback *,
+                          IUnknown *);
+  STDMETHODIMP Close();
+  STDMETHODIMP EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead);
+  STDMETHODIMP EndWrite(IMFAsyncResult *, ULONG *);
+  STDMETHODIMP Flush();
+  STDMETHODIMP GetCapabilities(DWORD *aCapabilities);
+  STDMETHODIMP GetCurrentPosition(QWORD *aPosition);
+  STDMETHODIMP GetLength(QWORD *pqwLength);
+  STDMETHODIMP IsEndOfStream(BOOL *aIsEndOfStream);
+  STDMETHODIMP Read(BYTE *, ULONG, ULONG *);
+  STDMETHODIMP Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
+                    LONGLONG aSeekOffset,
+                    DWORD aSeekFlags,
+                    QWORD *aCurrentPosition);
+  STDMETHODIMP SetCurrentPosition(QWORD aPosition);
+  STDMETHODIMP SetLength(QWORD);
+  STDMETHODIMP Write(const BYTE *, ULONG, ULONG *);
+
+  // IMFAsyncCallback methods.
+  // We perform an async read operation in this callback implementation.
+  STDMETHODIMP GetParameters(DWORD*, DWORD*);
+  STDMETHODIMP Invoke(IMFAsyncResult* aResult);
+
+private:
+
+  // Id of the work queue upon which we perfrom reads. This
+  // objects's Invoke() function is called on the work queue's thread,
+  // and it's there that we perform blocking IO. This has value
+  // MFASYNC_CALLBACK_QUEUE_UNDEFINED if the work queue hasn't been
+  // created yet.
+  DWORD mWorkQueueId;
+
+  // Stores data regarding an async read opreation.
+  class AsyncReadRequestState : public IUnknown {
+  public:
+    AsyncReadRequestState(int64_t aOffset, BYTE* aBuffer, ULONG aLength)
+      : mOffset(aOffset),
+        mBuffer(aBuffer),
+        mBufferLength(aLength),
+        mBytesRead(0)
+    {}
+
+    // IUnknown Methods
+    STDMETHODIMP QueryInterface(REFIID aRIID, LPVOID *aOutObject);
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    int64_t mOffset;
+    BYTE* mBuffer;
+    ULONG mBufferLength;
+    ULONG mBytesRead;
+
+    // IUnknown ref counting.
+    nsAutoRefCnt mRefCnt;
+    NS_DECL_OWNINGTHREAD;
+  };
+
+  // Resource we're wrapping. Note this object's methods are threadsafe,
+  // and we only call read/seek on the work queue's thread.
+  MediaResource* mResource;
+
+  // Protects mOffset, which is accessed by the SourceReaders thread(s), and
+  // on the work queue thread.
+  ReentrantMonitor mReentrantMonitor;
+
+  // Current offset of the logical read cursor. We maintain this separately
+  // from the media resource's offset since a partially complete read (in Invoke())
+  // would leave the resource's offset at a value unexpected by the caller,
+  // since the read hadn't yet completed.
+  int64_t mOffset;
+
+  // IUnknown ref counting.
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFDecoder.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "WMFDecoder.h"
+#include "WMFReader.h"
+#include "WMFUtils.h"
+#include "MediaDecoderStateMachine.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+
+MediaDecoderStateMachine* WMFDecoder::CreateStateMachine()
+{
+  return new MediaDecoderStateMachine(this, new WMFReader(this));
+}
+
+bool
+WMFDecoder::GetSupportedCodecs(const nsACString& aType,
+                               char const *const ** aCodecList)
+{
+  if (!MediaDecoder::IsWMFEnabled() ||
+      NS_FAILED(LoadDLLs()))
+    return false;
+
+  // MP3 is specified to have no codecs in its "type" param:
+  // http://wiki.whatwg.org/wiki/Video_type_parameters#MPEG
+  // So specify an empty codecs list, so that if script specifies 
+  // a "type" param with codecs, it will be reported as not supported
+  // as per the spec.
+  static char const *const mp3AudioCodecs[] = {
+    nullptr
+  };
+  if (aType.EqualsASCII("audio/mpeg")) {
+    if (aCodecList) {
+      *aCodecList = mp3AudioCodecs;
+    }
+    // Assume that if LoadDLLs() didn't fail, we can decode MP3.
+    return true;
+  }
+
+  // AAC in M4A.
+  static char const *const aacAudioCodecs[] = {
+    "mp4a.40.2",    // AAC-LC
+    nullptr
+  };
+  if (aType.EqualsASCII("audio/mp4")) {
+    if (aCodecList) {
+      *aCodecList = aacAudioCodecs;
+    }
+    return true;
+  }
+
+  // H.264 + AAC in MP4.
+  static char const *const H264Codecs[] = {
+    "avc1.42E01E",  // H.264 Constrained Baseline Profile Level 3.0
+    "avc1.42001E",  // H.264 Baseline Profile Level 3.0
+    "avc1.58A01E",  // H.264 Extended Profile Level 3.0
+    "avc1.4D401E",  // H.264 Main Profile Level 3.0
+    "avc1.64001E",  // H.264 High Profile Level 3.0
+    "avc1.64001F",  // H.264 High Profile Level 3.1
+    "mp4a.40.2",    // AAC-LC
+    nullptr
+  };
+  if (aType.EqualsASCII("video/mp4")) {
+    if (aCodecList) {
+      *aCodecList = H264Codecs;
+    }
+    return true;
+  }
+
+  return false;
+}
+
+nsresult
+WMFDecoder::LoadDLLs()
+{
+  return SUCCEEDED(wmf::LoadDLLs()) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+WMFDecoder::UnloadDLLs()
+{
+  wmf::UnloadDLLs();
+}
+
+bool IsWindows7OrLater()
+{
+  OSVERSIONINFO versionInfo;
+  BOOL isWin7OrLater = FALSE;
+  versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+  if (!GetVersionEx(&versionInfo)) {
+    return false;
+  }
+  // Note: Win Vista = 6.0
+  //       Win 7     = 6.1
+  //       Win 8     = 6.2
+  return versionInfo.dwMajorVersion > 6 ||
+        (versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1);
+}
+
+/* static */
+bool
+WMFDecoder::IsEnabled()
+{
+  // We only use WMF on Windows 7 and up, until we can properly test Vista
+  // and how it responds with and without the Platform Update installed.
+  return IsWindows7OrLater() &&
+         Preferences::GetBool("media.windows-media-foundation.enabled");
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFDecoder.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#if !defined(WMFDecoder_h_)
+#define WMFDecoder_h_
+
+#include "MediaDecoder.h"
+
+namespace mozilla {
+
+// Decoder that uses Windows Media Foundation to playback H.264/AAC in MP4
+// and M4A files, and MP3 files. Playback is strictly limited to only those
+// codecs.
+class WMFDecoder : public MediaDecoder
+{
+public:
+
+  virtual MediaDecoder* Clone() {
+    if (!IsWMFEnabled()) {
+      return nullptr;
+    }
+    return new WMFDecoder();
+  }
+
+  virtual MediaDecoderStateMachine* CreateStateMachine();
+
+  // Returns true if aType is a MIME type that we render with the
+  // Windows Media Foundation backend. If aCodecList is non null,
+  // it is filled with a (static const) null-terminated list of strings
+  // denoting the codecs we'll playback. Note that playback is strictly
+  // limited to the codecs in this list.
+  static bool GetSupportedCodecs(const nsACString& aType,
+                                 char const *const ** aCodecList);
+
+  // Loads the DLLs required by Windows Media Foundation. If this returns
+  // failure, you can assume that WMF is not available on the user's system.
+  static nsresult LoadDLLs();
+  static void UnloadDLLs();
+
+  // Returns true if the WMF backend is preffed on, and we're running on a
+  // version of Windows which is likely to support WMF.
+  static bool IsEnabled();
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFReader.cpp
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMFReader.h"
+#include "WMFDecoder.h"
+#include "WMFUtils.h"
+#include "WMFByteStream.h"
+
+#ifndef MOZ_SAMPLE_TYPE_FLOAT32
+#error We expect 32bit float audio samples on desktop for the Windows Media Foundation media backend.
+#endif
+
+#include "MediaDecoder.h"
+#include "VideoUtils.h"
+
+namespace mozilla {
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaDecoderLog;
+#define LOG(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+// Uncomment to enable verbose per-sample logging.
+//#define LOG_SAMPLE_DECODE 1
+
+WMFReader::WMFReader(MediaDecoder* aDecoder)
+  : MediaDecoderReader(aDecoder),
+    mSourceReader(nullptr),
+    mAudioChannels(0),
+    mAudioBytesPerSample(0),
+    mAudioRate(0),
+    mVideoWidth(0),
+    mVideoHeight(0),
+    mVideoStride(0),
+    mHasAudio(false),
+    mHasVideo(false),
+    mCanSeek(false)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  MOZ_COUNT_CTOR(WMFReader);
+}
+
+WMFReader::~WMFReader()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  // Note: We must shutdown the byte stream before calling MFShutdown, else we
+  // get assertion failures when unlocking the byte stream's work queue.
+  if (mByteStream) {
+    nsresult rv = mByteStream->Shutdown();
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown WMFByteStream");
+  }
+  HRESULT hr = wmf::MFShutdown();
+  NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
+  MOZ_COUNT_DTOR(WMFReader);
+}
+
+void
+WMFReader::OnDecodeThreadStart()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), );
+}
+
+void
+WMFReader::OnDecodeThreadFinish()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  CoUninitialize();
+}
+
+nsresult
+WMFReader::Init(MediaDecoderReader* aCloneDonor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  nsresult rv = WMFDecoder::LoadDLLs();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (FAILED(wmf::MFStartup())) {
+    NS_WARNING("Failed to initialize Windows Media Foundation");
+    return NS_ERROR_FAILURE;
+  }
+
+  // Must be created on main thread.
+  mByteStream = new WMFByteStream(mDecoder->GetResource());
+
+  return mByteStream->Init();
+}
+
+bool
+WMFReader::HasAudio()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  return mHasAudio;
+}
+
+bool
+WMFReader::HasVideo()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  return mHasVideo;
+}
+
+static HRESULT
+ConfigureSourceReaderStream(IMFSourceReader *aReader,
+                            const DWORD aStreamIndex,
+                            const GUID& aOutputSubType,
+                            const GUID* aAllowedInSubTypes,
+                            const uint32_t aNumAllowedInSubTypes)
+{
+  NS_ENSURE_TRUE(aReader, E_POINTER);
+  NS_ENSURE_TRUE(aAllowedInSubTypes, E_POINTER);
+
+  IMFMediaTypePtr nativeType;
+  IMFMediaTypePtr type;
+  HRESULT hr;
+
+  // Find the native format of the stream.
+  hr = aReader->GetNativeMediaType(aStreamIndex, 0, &nativeType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Get the native output subtype of the stream. This denotes the uncompressed
+  // type.
+  GUID subType;
+  hr = nativeType->GetGUID(MF_MT_SUBTYPE, &subType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Ensure the input type of the media is in the allowed formats list.
+  bool isSubTypeAllowed = false;
+  for (uint32_t i = 0; i < aNumAllowedInSubTypes; i++) {
+    if (aAllowedInSubTypes[i] == subType) {
+      isSubTypeAllowed = true;
+      break;
+    }
+  }
+  if (!isSubTypeAllowed) {
+    nsCString name = GetGUIDName(subType);
+    LOG("ConfigureSourceReaderStream subType=%s is not allowed to be decoded", name.get());
+    return E_FAIL;
+  }
+
+  // Find the major type.
+  GUID majorType;
+  hr = nativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Define the output type.
+  hr = wmf::MFCreateMediaType(&type);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = type->SetGUID(MF_MT_MAJOR_TYPE, majorType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = type->SetGUID(MF_MT_SUBTYPE, aOutputSubType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Set the uncompressed format. This can fail if the decoder can't produce
+  // that type.
+  return aReader->SetCurrentMediaType(aStreamIndex, NULL, type);
+}
+
+// Returns the duration of the resource, in microseconds.
+HRESULT
+GetSourceReaderDuration(IMFSourceReader *aReader,
+                        int64_t& aOutDuration)
+{
+  AutoPropVar var;
+  HRESULT hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
+                                                 MF_PD_DURATION,
+                                                 &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // WMF stores duration in hundred nanosecond units.
+  int64_t duration_hns = 0;
+  hr = wmf::PropVariantToInt64(var, &duration_hns);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  aOutDuration = HNsToUsecs(duration_hns);
+
+  return S_OK;
+}
+
+HRESULT
+GetSourceReaderCanSeek(IMFSourceReader* aReader, bool& aOutCanSeek)
+{
+  NS_ENSURE_TRUE(aReader, E_FAIL);
+
+  HRESULT hr;
+  AutoPropVar var;
+  hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
+                                         MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS,
+                                         &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  ULONG flags = 0;
+  hr = wmf::PropVariantToUInt32(var, &flags);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
+
+  return S_OK;
+}
+
+static HRESULT
+GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
+{
+  // Try to get the default stride from the media type.
+  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
+  if (SUCCEEDED(hr)) {
+    return S_OK;
+  }
+
+  // Stride attribute not set, calculate it.
+  GUID subtype = GUID_NULL;
+  uint32_t width = 0;
+  uint32_t height = 0;
+
+  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  return hr;
+}
+
+void
+WMFReader::ConfigureVideoDecoder()
+{
+  NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
+
+  // Determine if we have video.
+  if (!mSourceReader ||
+      !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM)) {
+    return;
+  }
+
+  static const GUID MP4VideoTypes[] = {
+    MFVideoFormat_H264
+  };
+  HRESULT hr = ConfigureSourceReaderStream(mSourceReader,
+                                           MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                           MFVideoFormat_YV12,
+                                           MP4VideoTypes,
+                                           NS_ARRAY_LENGTH(MP4VideoTypes));
+  if (FAILED(hr)) {
+    LOG("Failed to configured video output for MFVideoFormat_YV12");
+    return;
+  }
+
+  IMFMediaTypePtr mediaType;
+  hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                          &mediaType);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to get configured video media type");
+    return;
+  }
+
+  if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &mVideoWidth, &mVideoHeight))) {
+    NS_WARNING("WMF video decoder failed to get frame dimensions!");
+    return;
+  }
+
+  LOG("Video frame %u x %u", mVideoWidth, mVideoHeight);
+  uint32_t aspectNum = 0, aspectDenom = 0;
+  if (FAILED(MFGetAttributeRatio(mediaType,
+                                 MF_MT_PIXEL_ASPECT_RATIO,
+                                 &aspectNum,
+                                 &aspectDenom))) {
+    NS_WARNING("WMF video decoder failed to get pixel aspect ratio!");
+    return;
+  }
+  LOG("Video aspect ratio %u x %u", aspectNum, aspectDenom);
+
+  GetDefaultStride(mediaType, &mVideoStride);
+
+  // Calculate and validate the frame size.
+  nsIntSize frameSize = nsIntSize(mVideoWidth, mVideoHeight);
+  nsIntRect pictureRegion = nsIntRect(0, 0, mVideoWidth, mVideoHeight);
+  nsIntSize displaySize = frameSize;
+  ScaleDisplayByAspectRatio(displaySize, float(aspectNum)/float(aspectDenom));
+  if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
+    // Video track's frame sizes will overflow. Ignore the video track.
+    return;
+  }
+  mInfo.mDisplay = displaySize;
+
+  LOG("Successfully configured video stream");
+
+  mHasVideo = mInfo.mHasVideo = true;
+}
+
+void
+WMFReader::ConfigureAudioDecoder()
+{
+  NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
+
+  if (!mSourceReader ||
+      !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_AUDIO_STREAM)) {
+    // No audio stream.
+    return;
+  }
+
+  static const GUID MP4AudioTypes[] = {
+    MFAudioFormat_AAC,
+    MFAudioFormat_MP3
+  };
+  if (FAILED(ConfigureSourceReaderStream(mSourceReader,
+                                         MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                         MFAudioFormat_Float,
+                                         MP4AudioTypes,
+                                         NS_ARRAY_LENGTH(MP4AudioTypes)))) {
+    NS_WARNING("Failed to configure WMF Audio decoder for PCM output");
+    return;
+  }
+
+  IMFMediaTypePtr mediaType;
+  HRESULT hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                                  &mediaType);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to get configured audio media type");
+    return;
+  }
+
+  mAudioRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
+  mAudioChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0);
+  mAudioBytesPerSample = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16) / 8;
+
+  mInfo.mAudioChannels = mAudioChannels;
+  mInfo.mAudioRate = mAudioRate;
+  mHasAudio = mInfo.mHasAudio = true;
+
+  LOG("Successfully configured audio stream. rate=%u channels=%u bitsPerSample=%u",
+      mAudioRate, mAudioChannels, mAudioBytesPerSample);
+}
+
+nsresult
+WMFReader::ReadMetadata(VideoInfo* aInfo,
+                        MetadataTags** aTags)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  LOG("WMFReader::ReadMetadata()");
+  HRESULT hr;
+
+  hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, NULL, &mSourceReader);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  ConfigureVideoDecoder();
+  ConfigureAudioDecoder();
+
+  // Abort if both video and audio failed to initialize.
+  NS_ENSURE_TRUE(mInfo.mHasAudio || mInfo.mHasVideo, NS_ERROR_FAILURE);
+
+  int64_t duration = 0;
+  if (SUCCEEDED(GetSourceReaderDuration(mSourceReader, duration))) {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    mDecoder->SetMediaDuration(duration);
+  }
+
+  hr = GetSourceReaderCanSeek(mSourceReader, mCanSeek);
+  NS_ASSERTION(SUCCEEDED(hr), "Can't determine if resource is seekable");
+
+  *aInfo = mInfo;
+  *aTags = nullptr;
+  // aTags can be retrieved using techniques like used here:
+  // http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx
+
+  return NS_OK;
+}
+
+static int64_t
+GetSampleDuration(IMFSample* aSample)
+{
+  int64_t duration = 0;
+  aSample->GetSampleDuration(&duration);
+  return HNsToUsecs(duration);
+}
+
+bool
+WMFReader::DecodeAudioData()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  DWORD flags;
+  LONGLONG timestampHns;
+  HRESULT hr;
+
+  IMFSamplePtr sample;
+  hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                 0, // control flags
+                                 nullptr, // read stream index
+                                 &flags,
+                                 &timestampHns,
+                                 &sample);
+
+  if (FAILED(hr) ||
+      (flags & MF_SOURCE_READERF_ERROR) ||
+      (flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+    // End of stream.
+    mAudioQueue.Finish();
+    return false;
+  }
+
+  IMFMediaBufferPtr buffer;
+  hr = sample->ConvertToContiguousBuffer(&buffer);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
+  BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
+  DWORD maxLength = 0, currentLength = 0;
+  hr = buffer->Lock(&data, &maxLength, &currentLength);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
+  uint32_t numFrames = currentLength / mAudioBytesPerSample / mAudioChannels;
+  NS_ASSERTION(sizeof(AudioDataValue) == mAudioBytesPerSample, "Size calculation is wrong");
+  nsAutoArrayPtr<AudioDataValue> pcmSamples(new AudioDataValue[numFrames * mAudioChannels]);
+  memcpy(pcmSamples.get(), data, currentLength);
+  buffer->Unlock();
+
+  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t timestamp = HNsToUsecs(timestampHns);
+  int64_t duration = GetSampleDuration(sample);
+
+  mAudioQueue.Push(new AudioData(offset,
+                                 timestamp,
+                                 duration,
+                                 numFrames,
+                                 pcmSamples.forget(),
+                                 mAudioChannels));
+
+  #ifdef LOG_SAMPLE_DECODE
+  LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
+      timestamp, duration, currentLength);
+  #endif
+
+  return true;
+}
+
+bool
+WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
+                            int64_t aTimeThreshold)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  // Record number of frames decoded and parsed. Automatically update the
+  // stats counters using the AutoNotifyDecoded stack-based class.
+  uint32_t parsed = 0, decoded = 0;
+  AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
+
+  DWORD flags;
+  LONGLONG timestampHns;
+  HRESULT hr;
+
+  IMFSamplePtr sample;
+  hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                 0, // control flags
+                                 nullptr, // read stream index
+                                 &flags,
+                                 &timestampHns,
+                                 &sample);
+  if (flags & MF_SOURCE_READERF_ERROR) {
+    NS_WARNING("WMFReader: Catastrophic failure reading video sample");
+    // Future ReadSample() calls will fail, so give up and report end of stream.
+    mVideoQueue.Finish();
+    return false;
+  }
+
+  if (FAILED(hr)) {
+    // Unknown failure, ask caller to try again?
+    return true;
+  }
+
+  if (!sample) {
+    if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+      LOG("WMFReader; Null sample after video decode, at end of stream");
+      // End of stream.
+      mVideoQueue.Finish();
+      return false;
+    }
+    LOG("WMFReader; Null sample after video decode. Maybe insufficient data...");
+    return true;
+  }
+
+  int64_t timestamp = HNsToUsecs(timestampHns);
+  if (timestamp < aTimeThreshold) {
+    return true;
+  }
+  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t duration = GetSampleDuration(sample);
+
+  IMFMediaBufferPtr buffer;
+
+  // Must convert to contiguous buffer to use IMD2DBuffer interface.
+  hr = sample->ConvertToContiguousBuffer(&buffer);
+  if (FAILED(hr)) {
+    NS_WARNING("ConvertToContiguousBuffer() failed!");
+    return true;
+  }
+
+  // Try and use the IMF2DBuffer interface if available, otherwise fallback
+  // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
+  // but only some systems (Windows 8?) support it.
+  BYTE* data = nullptr;
+  LONG stride = 0;
+  IMF2DBufferPtr twoDBuffer = buffer;
+  if (twoDBuffer) {
+    hr = twoDBuffer->Lock2D(&data, &stride);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+  } else {
+    hr = buffer->Lock(&data, NULL, NULL);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+    stride = mVideoStride;
+  }
+
+  // YV12, planar format: [YYYY....][VVVV....][UUUU....]
+  // i.e., Y, then V, then U.
+  VideoData::YCbCrBuffer b;
+
+  // Y (Y') plane
+  b.mPlanes[0].mData = data;
+  b.mPlanes[0].mStride = stride;
+  b.mPlanes[0].mHeight = mVideoHeight;
+  b.mPlanes[0].mWidth = stride;
+  b.mPlanes[0].mOffset = 0;
+  b.mPlanes[0].mSkip = 0;
+
+  // The V and U planes are stored 16-row-aligned, so we need to add padding
+  // to the row heights to ensure the Y'CbCr planes are referenced properly.
+  uint32_t padding = 0;
+  if (mVideoHeight % 16 != 0) {
+    padding = 16 - (mVideoHeight % 16);
+  }
+  uint32_t y_size = stride * (mVideoHeight + padding);
+  uint32_t v_size = stride * (mVideoHeight + padding) / 4;
+  uint32_t halfStride = (stride + 1) / 2;
+  uint32_t halfHeight = (mVideoHeight + 1) / 2;
+
+  // U plane (Cb)
+  b.mPlanes[1].mData = data + y_size + v_size;
+  b.mPlanes[1].mStride = halfStride;
+  b.mPlanes[1].mHeight = halfHeight;
+  b.mPlanes[1].mWidth = halfStride;
+  b.mPlanes[1].mOffset = 0;
+  b.mPlanes[1].mSkip = 0;
+
+  // V plane (Cr)
+  b.mPlanes[2].mData = data + y_size;
+  b.mPlanes[2].mStride = halfStride;
+  b.mPlanes[2].mHeight = halfHeight;
+  b.mPlanes[2].mWidth = halfStride;
+  b.mPlanes[2].mOffset = 0;
+  b.mPlanes[2].mSkip = 0;
+
+  VideoData *v = VideoData::Create(mInfo,
+                                   mDecoder->GetImageContainer(),
+                                   offset,
+                                   timestamp,
+                                   timestamp + duration,
+                                   b,
+                                   false,
+                                   -1,
+                                   nsIntRect(0, 0, mVideoWidth, mVideoHeight));
+  if (twoDBuffer) {
+    twoDBuffer->Unlock2D();
+  } else {
+    buffer->Unlock();
+  }
+
+  if (!v) {
+    NS_WARNING("Failed to create VideoData");
+    return false;
+  }
+  parsed++;
+  decoded++;
+  mVideoQueue.Push(v);
+
+  #ifdef LOG_SAMPLE_DECODE
+  LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d width=%u height=%u flags=%u",
+      timestamp, duration, stride, mVideoWidth, mVideoHeight, flags);
+  #endif
+
+  if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+    // End of stream.
+    mVideoQueue.Finish();
+    LOG("End of video stream");
+    return false;
+  }
+
+  return true;
+}
+
+nsresult
+WMFReader::Seek(int64_t aTargetUs,
+                int64_t aStartTime,
+                int64_t aEndTime,
+                int64_t aCurrentTime)
+{
+  LOG("WMFReader::Seek() %lld", aTargetUs);
+
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  if (!mCanSeek) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = ResetDecode();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoPropVar var;
+  HRESULT hr = InitPropVariantFromInt64(UsecsToHNs(aTargetUs), &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  hr = mSourceReader->SetCurrentPosition(GUID_NULL, var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  return DecodeToTarget(aTargetUs);
+}
+
+nsresult
+WMFReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime)
+{
+  MediaResource* stream = mDecoder->GetResource();
+  int64_t durationUs = 0;
+  {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    durationUs = mDecoder->GetMediaDuration();
+  }
+  GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFReader.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#if !defined(WMFReader_h_)
+#define WMFReader_h_
+
+#include "WMF.h"
+#include "MediaDecoderReader.h"
+
+namespace mozilla {
+
+class WMFByteStream;
+
+// Decoder backend for reading H.264/AAC in MP4/M4A and MP3 audio files,
+// using Windows Media Foundation.
+class WMFReader : public MediaDecoderReader
+{
+public:
+  WMFReader(MediaDecoder* aDecoder);
+
+  virtual ~WMFReader();
+
+  nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
+
+  bool DecodeAudioData() MOZ_OVERRIDE;
+  bool DecodeVideoFrame(bool &aKeyframeSkip,
+                        int64_t aTimeThreshold) MOZ_OVERRIDE;
+
+  bool HasAudio() MOZ_OVERRIDE;
+  bool HasVideo() MOZ_OVERRIDE;
+
+  nsresult ReadMetadata(VideoInfo* aInfo,
+                        MetadataTags** aTags) MOZ_OVERRIDE;
+
+  nsresult Seek(int64_t aTime,
+                int64_t aStartTime,
+                int64_t aEndTime,
+                int64_t aCurrentTime) MOZ_OVERRIDE;
+
+  nsresult GetBuffered(nsTimeRanges* aBuffered,
+                       int64_t aStartTime) MOZ_OVERRIDE;
+
+  void OnDecodeThreadStart() MOZ_OVERRIDE;
+  void OnDecodeThreadFinish() MOZ_OVERRIDE;
+
+private:
+
+  void ConfigureAudioDecoder();
+  void ConfigureVideoDecoder();
+
+  IMFSourceReaderPtr mSourceReader;
+  RefPtr<WMFByteStream> mByteStream;
+
+  uint32_t mAudioChannels;
+  uint32_t mAudioBytesPerSample;
+  uint32_t mAudioRate;
+
+  uint32_t mVideoWidth;
+  uint32_t mVideoHeight;
+  uint32_t mVideoStride;
+
+  bool mHasAudio;
+  bool mHasVideo;
+  bool mCanSeek;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFUtils.cpp
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMFUtils.h"
+#include "mozilla/StandardInteger.h"
+#include "prlog.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+struct GuidToName {
+  GUID guid;
+  const char* name;
+};
+
+#define GUID_TO_NAME_ENTRY(g) { g, #g }
+#define INTERFACE_TO_NAME_ENTRY(i) {IID_##i, #i }
+
+GuidToName GuidToNameTable[] = {
+  GUID_TO_NAME_ENTRY(MF_MT_MAJOR_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_MAJOR_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_SUBTYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_ALL_SAMPLES_INDEPENDENT),
+  GUID_TO_NAME_ENTRY(MF_MT_FIXED_SIZE_SAMPLES),
+  GUID_TO_NAME_ENTRY(MF_MT_COMPRESSED),
+  GUID_TO_NAME_ENTRY(MF_MT_SAMPLE_SIZE),
+  GUID_TO_NAME_ENTRY(MF_MT_WRAPPED_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_NUM_CHANNELS),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_AVG_BYTES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_BLOCK_ALIGNMENT),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_BITS_PER_SAMPLE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_SAMPLES_PER_BLOCK),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_CHANNEL_MASK),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_FOLDDOWN_MATRIX),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_PEAKREF),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_PEAKTARGET),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_AVGREF),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_AVGTARGET),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_PREFER_WAVEFORMATEX),
+  GUID_TO_NAME_ENTRY(MF_MT_AAC_PAYLOAD_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_SIZE),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE_RANGE_MAX),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE_RANGE_MIN),
+  GUID_TO_NAME_ENTRY(MF_MT_PIXEL_ASPECT_RATIO),
+  GUID_TO_NAME_ENTRY(MF_MT_DRM_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_PAD_CONTROL_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_SOURCE_CONTENT_HINT),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_CHROMA_SITING),
+  GUID_TO_NAME_ENTRY(MF_MT_INTERLACE_MODE),
+  GUID_TO_NAME_ENTRY(MF_MT_TRANSFER_FUNCTION),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_PRIMARIES),
+  GUID_TO_NAME_ENTRY(MF_MT_CUSTOM_VIDEO_PRIMARIES),
+  GUID_TO_NAME_ENTRY(MF_MT_YUV_MATRIX),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_LIGHTING),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_NOMINAL_RANGE),
+  GUID_TO_NAME_ENTRY(MF_MT_GEOMETRIC_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_MINIMUM_DISPLAY_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_PAN_SCAN_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_PAN_SCAN_ENABLED),
+  GUID_TO_NAME_ENTRY(MF_MT_AVG_BITRATE),
+  GUID_TO_NAME_ENTRY(MF_MT_AVG_BIT_ERROR_RATE),
+  GUID_TO_NAME_ENTRY(MF_MT_MAX_KEYFRAME_SPACING),
+  GUID_TO_NAME_ENTRY(MF_MT_DEFAULT_STRIDE),
+  GUID_TO_NAME_ENTRY(MF_MT_PALETTE),
+  GUID_TO_NAME_ENTRY(MF_MT_USER_DATA),
+  GUID_TO_NAME_ENTRY(MF_MT_AM_FORMAT_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG_START_TIME_CODE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_PROFILE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_LEVEL),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG_SEQUENCE_HEADER),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_SRC_PACK_0),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_CTRL_PACK_0),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_SRC_PACK_1),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_CTRL_PACK_1),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_VAUX_SRC_PACK),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_VAUX_CTRL_PACK),
+  GUID_TO_NAME_ENTRY(MF_MT_ARBITRARY_HEADER),
+  GUID_TO_NAME_ENTRY(MF_MT_ARBITRARY_FORMAT),
+  GUID_TO_NAME_ENTRY(MF_MT_IMAGE_LOSS_TOLERANT),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG4_SAMPLE_DESCRIPTION),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY),
+  GUID_TO_NAME_ENTRY(MF_MT_ORIGINAL_4CC),
+  GUID_TO_NAME_ENTRY(MF_MT_ORIGINAL_WAVE_FORMAT_TAG),
+
+  GUID_TO_NAME_ENTRY(MFMediaType_Audio),
+  GUID_TO_NAME_ENTRY(MFMediaType_Video),
+  GUID_TO_NAME_ENTRY(MFMediaType_Protected),
+  GUID_TO_NAME_ENTRY(MFMediaType_SAMI),
+  GUID_TO_NAME_ENTRY(MFMediaType_Script),
+  GUID_TO_NAME_ENTRY(MFMediaType_Image),
+  GUID_TO_NAME_ENTRY(MFMediaType_HTML),
+  GUID_TO_NAME_ENTRY(MFMediaType_Binary),
+  GUID_TO_NAME_ENTRY(MFMediaType_FileTransfer),
+
+  GUID_TO_NAME_ENTRY(MFVideoFormat_AI44),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_ARGB32),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_AYUV),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DV25),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DV50),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVH1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVSD),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVSL),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_H264),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_I420),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_IYUV),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_M4S2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MJPG),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP43),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP4S),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP4V),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MPG1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MSS1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MSS2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_NV11),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_NV12),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P010),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P016),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P216),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB24),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB32),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB555),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB565),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB8),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_UYVY),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_v210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_v410),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV3),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WVC1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y216),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y410),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y416),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y41P),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y41T),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YUY2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YV12),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YVYU),
+
+  GUID_TO_NAME_ENTRY(MFAudioFormat_PCM),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_Float),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_DTS),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_Dolby_AC3_SPDIF),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_DRM),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudioV8),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudioV9),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudio_Lossless),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMASPDIF),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MSP1),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MP3),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MPEG),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_AAC),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_ADTS),
+
+  // Interfaces which may be implemented by WMFByteStream.
+  INTERFACE_TO_NAME_ENTRY(IUnknown),
+  INTERFACE_TO_NAME_ENTRY(IMFByteStream),
+  INTERFACE_TO_NAME_ENTRY(IMFMediaSource),
+  INTERFACE_TO_NAME_ENTRY(IMFAttributes),
+  INTERFACE_TO_NAME_ENTRY(IMFByteStreamBuffering),
+};
+
+nsCString GetGUIDName(const GUID& guid)
+{
+  unsigned numTypes = NS_ARRAY_LENGTH(GuidToNameTable);
+  for (unsigned i = 0; i < numTypes; i++) {
+    if (guid == GuidToNameTable[i].guid) {
+      return nsDependentCString(GuidToNameTable[i].name);
+    }
+  }
+
+  WCHAR* name = nullptr;
+  HRESULT hr = StringFromCLSID(guid , &name);
+  if (FAILED(hr)) {
+    return nsDependentCString("GuidUnknown");
+  }
+  nsCString name_u8(NS_ConvertUTF16toUTF8(nsDependentString((PRUnichar*)(name))));
+  CoTaskMemFree(name);
+  return name_u8;
+}
+
+bool
+SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex)
+{
+  IMFMediaTypePtr nativeType;
+  HRESULT hr = aReader->GetNativeMediaType(aIndex, 0, &nativeType);
+  return FAILED(hr) ? false : true;
+}
+
+namespace wmf {
+
+static bool sDLLsLoaded = false;
+static bool sFailedToLoadDlls = false;
+
+static HMODULE sMfPlatMod = NULL;
+
+struct WMFModule {
+  const char* name;
+  HMODULE handle;
+};
+
+static WMFModule sDLLs[] = {
+  { "C:\\Windows\\system32\\mfplat.dll", NULL },
+  { "C:\\Windows\\system32\\mfreadwrite.dll", NULL },
+  { "C:\\Windows\\system32\\propsys.dll", NULL },
+  { "C:\\Windows\\system32\\mf.dll", NULL },
+};
+
+HRESULT
+LoadDLLs()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (sDLLsLoaded) {
+    return S_OK;
+  }
+  if (sFailedToLoadDlls) {
+    return E_FAIL;
+  }
+
+  // Try to load all the required DLLs.
+  uint32_t dllLength = NS_ARRAY_LENGTH(sDLLs);
+  for (uint32_t i = 0; i < dllLength; i++) {
+    sDLLs[i].handle = LoadLibraryA(sDLLs[i].name);
+    if (!sDLLs[i].handle) {
+      sFailedToLoadDlls = true;
+      UnloadDLLs();
+      wmf::MFShutdown();
+      return E_FAIL;
+    }
+  }
+
+  sDLLsLoaded = true;
+
+  return S_OK;
+}
+
+HRESULT
+UnloadDLLs()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  uint32_t length = NS_ARRAY_LENGTH(sDLLs);
+  for (uint32_t i = 0; i < length; i++) {
+    if (sDLLs[i].handle) {
+      FreeLibrary(sDLLs[i].handle);
+      sDLLs[i].handle = NULL;
+    }
+    sDLLsLoaded = false;
+  }
+  return S_OK;
+}
+
+#define ENSURE_FUNCTION_PTR(FunctionName, DLL) \
+  static FunctionName##Ptr_t FunctionName##Ptr = nullptr; \
+  if (!FunctionName##Ptr) { \
+    FunctionName##Ptr = (FunctionName##Ptr_t)GetProcAddress(GetModuleHandle( #DLL ), #FunctionName ); \
+    if (!FunctionName##Ptr) { \
+      NS_WARNING("Failed to get GetProcAddress of " #FunctionName " from " #DLL ); \
+      return E_FAIL; \
+    } \
+  }
+
+#define DECL_FUNCTION_PTR(FunctionName, ...) \
+  typedef HRESULT (STDMETHODCALLTYPE * FunctionName##Ptr_t)(__VA_ARGS__)
+
+HRESULT
+MFStartup()
+{
+  DECL_FUNCTION_PTR(MFStartup, ULONG, DWORD);
+  ENSURE_FUNCTION_PTR(MFStartup, Mfplat.dll)
+  return MFStartupPtr(MF_VERSION, MFSTARTUP_FULL);
+}
+
+HRESULT
+MFShutdown()
+{
+  DECL_FUNCTION_PTR(MFShutdown);
+  ENSURE_FUNCTION_PTR(MFShutdown, Mfplat.dll)
+  return (MFShutdownPtr)();
+}
+
+HRESULT
+MFPutWorkItem(DWORD aQueueId,
+              IMFAsyncCallback *aCallback,
+              IUnknown *aState)
+{
+  DECL_FUNCTION_PTR(MFPutWorkItem, DWORD, IMFAsyncCallback*, IUnknown*);
+  ENSURE_FUNCTION_PTR(MFPutWorkItem, Mfplat.dll)
+  return (MFPutWorkItemPtr)(aQueueId, aCallback, aState);
+}
+
+HRESULT
+MFAllocateWorkQueue(DWORD *aOutWorkQueueId)
+{
+  DECL_FUNCTION_PTR(MFAllocateWorkQueue, DWORD*);
+  ENSURE_FUNCTION_PTR(MFAllocateWorkQueue, Mfplat.dll)
+  return (MFAllocateWorkQueuePtr)(aOutWorkQueueId);
+}
+
+HRESULT
+MFUnlockWorkQueue(DWORD aWorkQueueId)
+{
+  DECL_FUNCTION_PTR(MFUnlockWorkQueue, DWORD);
+  ENSURE_FUNCTION_PTR(MFUnlockWorkQueue, Mfplat.dll);
+  return (MFUnlockWorkQueuePtr)(aWorkQueueId);
+}
+
+HRESULT MFCreateAsyncResult(IUnknown *aUnkObject,
+                            IMFAsyncCallback *aCallback,
+                            IUnknown *aUnkState,
+                            IMFAsyncResult **aOutAsyncResult)
+{
+  DECL_FUNCTION_PTR(MFCreateAsyncResult, IUnknown*, IMFAsyncCallback*, IUnknown*, IMFAsyncResult**);
+  ENSURE_FUNCTION_PTR(MFCreateAsyncResult, Mfplat.dll)
+  return (MFCreateAsyncResultPtr)(aUnkObject, aCallback, aUnkState, aOutAsyncResult);
+}
+
+HRESULT
+MFInvokeCallback(IMFAsyncResult *aAsyncResult)
+{
+  DECL_FUNCTION_PTR(MFInvokeCallback, IMFAsyncResult*);
+  ENSURE_FUNCTION_PTR(MFInvokeCallback, Mfplat.dll);
+  return (MFInvokeCallbackPtr)(aAsyncResult);
+}
+
+HRESULT
+MFCreateMediaType(IMFMediaType **aOutMFType)
+{
+  DECL_FUNCTION_PTR(MFCreateMediaType, IMFMediaType**);
+  ENSURE_FUNCTION_PTR(MFCreateMediaType, Mfplat.dll)
+  return (MFCreateMediaTypePtr)(aOutMFType);
+}
+
+HRESULT
+MFCreateSourceReaderFromByteStream(IMFByteStream *aByteStream,
+                                   IMFAttributes *aAttributes,
+                                   IMFSourceReader **aOutSourceReader)
+{
+  DECL_FUNCTION_PTR(MFCreateSourceReaderFromByteStream, IMFByteStream*, IMFAttributes*, IMFSourceReader**);
+  ENSURE_FUNCTION_PTR(MFCreateSourceReaderFromByteStream, Mfreadwrite.dll)
+  return (MFCreateSourceReaderFromByteStreamPtr)(aByteStream,
+                                                 aAttributes,
+                                                 aOutSourceReader);
+}
+
+HRESULT
+PropVariantToUInt32(REFPROPVARIANT aPropvar, ULONG *aOutUL)
+{
+  DECL_FUNCTION_PTR(PropVariantToUInt32, REFPROPVARIANT, ULONG *);
+  ENSURE_FUNCTION_PTR(PropVariantToUInt32, Propsys.dll)
+  return (PropVariantToUInt32Ptr)(aPropvar, aOutUL);
+}
+
+HRESULT PropVariantToInt64(REFPROPVARIANT aPropVar, LONGLONG *aOutLL)
+{
+  DECL_FUNCTION_PTR(PropVariantToInt64, REFPROPVARIANT, LONGLONG *);
+  ENSURE_FUNCTION_PTR(PropVariantToInt64, Propsys.dll)
+  return (PropVariantToInt64Ptr)(aPropVar, aOutLL);
+}
+
+HRESULT MFTGetInfo(CLSID aClsidMFT,
+                   LPWSTR *aOutName,
+                   MFT_REGISTER_TYPE_INFO **aOutInputTypes,
+                   UINT32 *aOutNumInputTypes,
+                   MFT_REGISTER_TYPE_INFO **aOutOutputTypes,
+                   UINT32 *aOutNumOutputTypes,
+                   IMFAttributes **aOutAttributes)
+{
+  DECL_FUNCTION_PTR(MFTGetInfo, CLSID, LPWSTR*, MFT_REGISTER_TYPE_INFO**, UINT32*, MFT_REGISTER_TYPE_INFO**, UINT32*, IMFAttributes**);
+  ENSURE_FUNCTION_PTR(MFTGetInfo, Mfplat.dll)
+  return (MFTGetInfoPtr)(aClsidMFT,
+                         aOutName,
+                         aOutInputTypes,
+                         aOutNumInputTypes,
+                         aOutOutputTypes,
+                         aOutNumOutputTypes,
+                         aOutAttributes);
+}
+
+HRESULT MFGetStrideForBitmapInfoHeader(DWORD aFormat,
+                                       DWORD aWidth,
+                                       LONG *aOutStride)
+{
+  DECL_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, DWORD, DWORD, LONG*);
+  ENSURE_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, Mfplat.dll)
+  return (MFGetStrideForBitmapInfoHeaderPtr)(aFormat, aWidth, aOutStride);
+}
+
+HRESULT MFCreateSourceReaderFromURL(LPCWSTR aURL,
+                                    IMFAttributes *aAttributes,
+                                    IMFSourceReader **aSourceReader)
+{
+  DECL_FUNCTION_PTR(MFCreateSourceReaderFromURL, LPCWSTR, IMFAttributes*, IMFSourceReader**);
+  ENSURE_FUNCTION_PTR(MFCreateSourceReaderFromURL, Mfreadwrite.dll)
+  return (MFCreateSourceReaderFromURLPtr)(aURL, aAttributes, aSourceReader);
+}
+
+} // end namespace wmf
+} // end namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFUtils.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "nsString.h"
+
+// Various utilities shared by WMF backend files.
+
+namespace mozilla {
+
+nsCString GetGUIDName(const GUID& guid);
+
+// Returns true if the reader has a stream with the specified index.
+// Index can be a specific index, or one of:
+//   MF_SOURCE_READER_FIRST_VIDEO_STREAM
+//   MF_SOURCE_READER_FIRST_AUDIO_STREAM
+bool
+SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex);
+
+// Auto manages the lifecycle of a PROPVARIANT.
+class AutoPropVar {
+public:
+  AutoPropVar() {
+    PropVariantInit(&mVar);
+  }
+  ~AutoPropVar() {
+    PropVariantClear(&mVar);
+  }
+  operator PROPVARIANT&() {
+    return mVar;
+  }
+  PROPVARIANT* operator->() {
+    return &mVar;
+  }
+  PROPVARIANT* operator&() {
+    return &mVar;
+  }
+private:
+  PROPVARIANT mVar;
+};
+
+// Converts from microseconds to hundreds of nanoseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t UsecsToHNs(int64_t aUsecs) {
+  return aUsecs * 10;
+}
+
+// Converts from hundreds of nanoseconds to microseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t HNsToUsecs(int64_t hNanoSecs) {
+  return hNanoSecs / 10;
+}
+
+} // namespace mozilla
--- a/docshell/shistory/public/nsISHistoryInternal.idl
+++ b/docshell/shistory/public/nsISHistoryInternal.idl
@@ -13,21 +13,20 @@ interface nsIDocShell;
 
 %{C++
 #define NS_SHISTORY_INTERNAL_CID \
 { 0x9c47c121, 0x1c6e, 0x4d8f, \
   { 0xb9, 0x04, 0x3a, 0xc9, 0x68, 0x11, 0x6e, 0x88 } }
 
 #define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
 
-template<class E, class A> class nsTArray;
-struct nsTArrayDefaultAllocator;
+#include "nsTArrayForwardDeclare.h"
 %}
 
-[ref] native nsDocshellIDArray(nsTArray<uint64_t, nsTArrayDefaultAllocator>);
+[ref] native nsDocshellIDArray(nsTArray<uint64_t>);
 
 [scriptable, uuid(f9348014-0239-11e2-b029-3d38e719eb2d)]
 interface nsISHistoryInternal: nsISupports
 {
   /**
    * Add a new Entry to the History List
    * @param aEntry - The entry to add
    * @param aPersist - If true this specifies that the entry should persist
--- a/dom/apps/src/Makefile.in
+++ b/dom/apps/src/Makefile.in
@@ -23,11 +23,12 @@ EXTRA_PP_JS_MODULES += \
   Webapps.jsm \
   $(NULL)
 
 EXTRA_JS_MODULES += \
   AppsServiceChild.jsm \
   AppsUtils.jsm \
   OfflineCacheInstaller.jsm \
   PermissionsInstaller.jsm \
+  PermissionsTable.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -5,22 +5,19 @@
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionSettings.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
-this.EXPORTED_SYMBOLS = ["PermissionsInstaller",
-                         "expandPermissions",
-                         "PermissionsTable",
-                         "appendAccessToPermName"
-                        ];
+this.EXPORTED_SYMBOLS = ["PermissionsInstaller"];
 const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
 const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
 const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
 const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
 
 // Permission access flags
 const READONLY = "readonly";
 const CREATEONLY = "createonly";
@@ -28,355 +25,16 @@ const READCREATE = "readcreate";
 const READWRITE = "readwrite";
 
 const PERM_TO_STRING = ["unknown", "allow", "deny", "prompt"];
 
 function debug(aMsg) {
   //dump("-*-*- PermissionsInstaller.jsm : " + aMsg + "\n");
 }
 
-// Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
-// Also, keep in sync with https://mxr.mozilla.org/mozilla-central/source/extensions/cookie/Permission.txt
-
-// Permissions that are implicit:
-// battery-status, network-information, vibration,
-// device-capabilities
-
-this.PermissionsTable =  { geolocation: {
-                             app: PROMPT_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           camera: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           alarms: {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "tcp-socket": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "network-events": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           contacts: {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:apps": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read"]
-                           },
-                           "device-storage:pictures": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:videos": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:music": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:sdcard": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           sms: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           telephony: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           browser: {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           bluetooth: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           mobileconnection: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           power: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           settings: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write"],
-                             additional: ["indexedDB-chrome-settings"]
-                           },
-                           permissions: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           fmradio: {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           attention: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "webapps-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "backgroundservice": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "desktop-notification": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "networkstats-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "wifi-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "systemXHR": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "voicemail": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "deprecated-hwvideo": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "idle": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "time": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "embed-apps": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "storage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             substitute: [
-                               "indexedDB-unlimited",
-                               "offline-app",
-                               "pin-app"
-                             ]
-                           },
-                           "background-sensors": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           cellbroadcast: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-normal": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-content": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-notification": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-alarm": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-telephony": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-ringer": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-publicnotification": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "open-remote-window": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                         };
-
-/**
- * Append access modes to the permission name as suffixes.
- *   e.g. permission name 'contacts' with ['read', 'write'] =
- *   ['contacts-read', contacts-write']
- * @param string aPermName
- * @param array aAccess
- * @returns array containing access-appended permission names.
- **/
-this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
-  if (aAccess.length == 0) {
-    return [aPermName];
-  }
-  return aAccess.map(function(aMode) {
-    return aPermName + "-" + aMode;
-  });
-};
-
-/**
- * Expand an access string into multiple permission names,
- *   e.g: permission name 'contacts' with 'readwrite' =
- *   ['contacts-read', 'contacts-create', 'contacts-write']
- * @param string aPermName
- * @param string aAccess (optional)
- * @returns array containing expanded permission names.
- **/
-this.expandPermissions = function expandPermissions(aPermName, aAccess) {
-  if (!PermissionsTable[aPermName]) {
-    Cu.reportError("PermissionsInstaller.jsm: expandPermissions: Unknown Permission: " + aPermName);
-    dump("PermissionsInstaller.jsm: expandPermissions: Unknown Permission: " + aPermName);
-    return [];
-  }
-
-  const tableEntry = PermissionsTable[aPermName];
-
-  if (tableEntry.substitute && tableEntry.additional) {
-    Cu.reportError("PermissionsInstaller.jsm: expandPermissions: Can't handle both 'substitute' " +
-                   "and 'additional' entries for permission: " + aPermName);
-    dump("PermissionsInstaller.jsm: expandPermissions: Can't handle both 'substitute' " +
-         "and 'additional' entries for permission: " + aPermName);
-    return [];
-  }
-
-  if (!aAccess && tableEntry.access ||
-      aAccess && !tableEntry.access) {
-    Cu.reportError("PermissionsTable.jsm: expandPermissions: Invalid Manifest : " +
-                   aPermName + " " + aAccess + "\n");
-    dump("PermissionsInstaller.jsm: expandPermissions: Invalid Manifest: " +
-         aPermName + " " + aAccess + "\n");
-    throw new Error("PermissionsInstaller.jsm: expandPermissions: Invalid Manifest: " +
-                    aPermName + " " + aAccess + "\n");
-  }
-
-  let expandedPermNames = [];
-
-  if (tableEntry.access && aAccess) {
-    let requestedSuffixes = [];
-    switch (aAccess) {
-    case READONLY:
-      requestedSuffixes.push("read");
-      break;
-    case CREATEONLY:
-      requestedSuffixes.push("create");
-      break;
-    case READCREATE:
-      requestedSuffixes.push("read", "create");
-      break;
-    case READWRITE:
-      requestedSuffixes.push("read", "create", "write");
-      break;
-    default:
-      return [];
-    }
-
-    let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
-
-    // Add the same suffix to each of the additions.
-    if (tableEntry.additional) {
-      for each (let additional in tableEntry.additional) {
-        permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
-      }
-    }
-
-    // Only add the suffixed version if the suffix exisits in the table.
-    for (let idx in permArr) {
-      let suffix = requestedSuffixes[idx % requestedSuffixes.length];
-      if (tableEntry.access.indexOf(suffix) != -1) {
-        expandedPermNames.push(permArr[idx]);
-      }
-    }
-  } else if (tableEntry.substitute) {
-    expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
-  } else {
-    expandedPermNames.push(aPermName);
-    // Include each of the additions exactly as they appear in the table.
-    if (tableEntry.additional) {
-      expandedPermNames = expandedPermNames.concat(tableEntry.additional);
-    }
-  }
-
-  return expandedPermNames;
-};
-
 // An array carring all the possible (expanded) permission names.
 let AllPossiblePermissions = [];
 for (let permName in PermissionsTable) {
   let expandedPermNames = [];
   if (PermissionsTable[permName].access) {
     expandedPermNames = expandPermissions(permName, READWRITE);
   } else {
     expandedPermNames = expandPermissions(permName);
new file mode 100644
--- /dev/null
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -0,0 +1,421 @@
+/* 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 Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [
+  "PermissionsTable",
+  "PermissionsReverseTable",
+  "expandPermissions",
+  "appendAccessToPermName",
+  "isExplicitInPermissionsTable"
+];
+
+// Permission access flags
+const READONLY = "readonly";
+const CREATEONLY = "createonly";
+const READCREATE = "readcreate";
+const READWRITE = "readwrite";
+
+const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
+const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
+const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
+const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
+
+// Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
+// Also, keep in sync with https://mxr.mozilla.org/mozilla-central/source/extensions/cookie/Permission.txt
+
+// Permissions that are implicit:
+// battery-status, network-information, vibration,
+// device-capabilities
+
+this.PermissionsTable =  { geolocation: {
+                             app: PROMPT_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           camera: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           alarms: {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "tcp-socket": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "network-events": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           contacts: {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:apps": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read"]
+                           },
+                           "device-storage:pictures": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:videos": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:music": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:sdcard": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           sms: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           telephony: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           browser: {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           bluetooth: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           mobileconnection: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           power: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           settings: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write"],
+                             additional: ["indexedDB-chrome-settings"]
+                           },
+                           permissions: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           fmradio: {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           attention: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "webapps-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "backgroundservice": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "desktop-notification": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "networkstats-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "wifi-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "systemXHR": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "voicemail": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "deprecated-hwvideo": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "idle": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "time": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "embed-apps": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "storage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             substitute: [
+                               "indexedDB-unlimited",
+                               "offline-app",
+                               "pin-app"
+                             ]
+                           },
+                           "background-sensors": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           cellbroadcast: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-normal": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-content": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-notification": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-alarm": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-telephony": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-ringer": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-publicnotification": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "open-remote-window": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                         };
+
+/**
+ * Append access modes to the permission name as suffixes.
+ *   e.g. permission name 'contacts' with ['read', 'write'] =
+ *   ['contacts-read', contacts-write']
+ * @param string aPermName
+ * @param array aAccess
+ * @returns array containing access-appended permission names.
+ **/
+this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
+  if (aAccess.length == 0) {
+    return [aPermName];
+  }
+  return aAccess.map(function(aMode) {
+    return aPermName + "-" + aMode;
+  });
+};
+
+/**
+ * Expand an access string into multiple permission names,
+ *   e.g: permission name 'contacts' with 'readwrite' =
+ *   ['contacts-read', 'contacts-create', 'contacts-write']
+ * @param string aPermName
+ * @param string aAccess (optional)
+ * @returns array containing expanded permission names.
+ **/
+this.expandPermissions = function expandPermissions(aPermName, aAccess) {
+  if (!PermissionsTable[aPermName]) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName;
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    return [];
+  }
+
+  const tableEntry = PermissionsTable[aPermName];
+
+  if (tableEntry.substitute && tableEntry.additional) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Can't handle both 'substitute' " +
+      "and 'additional' entries for permission: " + aPermName;
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    return [];
+  }
+
+  if (!aAccess && tableEntry.access ||
+      aAccess && !tableEntry.access) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Invalid Manifest : " +
+      aPermName + " " + aAccess + "\n";
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    throw new Error(errorMsg);
+  }
+
+  let expandedPermNames = [];
+
+  if (tableEntry.access && aAccess) {
+    let requestedSuffixes = [];
+    switch (aAccess) {
+    case READONLY:
+      requestedSuffixes.push("read");
+      break;
+    case CREATEONLY:
+      requestedSuffixes.push("create");
+      break;
+    case READCREATE:
+      requestedSuffixes.push("read", "create");
+      break;
+    case READWRITE:
+      requestedSuffixes.push("read", "create", "write");
+      break;
+    default:
+      return [];
+    }
+
+    let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
+
+    // Add the same suffix to each of the additions.
+    if (tableEntry.additional) {
+      for each (let additional in tableEntry.additional) {
+        permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
+      }
+    }
+
+    // Only add the suffixed version if the suffix exists in the table.
+    for (let idx in permArr) {
+      let suffix = requestedSuffixes[idx % requestedSuffixes.length];
+      if (tableEntry.access.indexOf(suffix) != -1) {
+        expandedPermNames.push(permArr[idx]);
+      }
+    }
+  } else if (tableEntry.substitute) {
+    expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
+  } else {
+    expandedPermNames.push(aPermName);
+    // Include each of the additions exactly as they appear in the table.
+    if (tableEntry.additional) {
+      expandedPermNames = expandedPermNames.concat(tableEntry.additional);
+    }
+  }
+
+  return expandedPermNames;
+};
+
+this.PermissionsReverseTable = (function () {
+  // PermissionsTable as it is works well for direct searches, but not
+  // so well for reverse ones (that is, if I get something like
+  // device-storage:music-read or indexedDB-chrome-settings-read how
+  // do I know which permission it really is? Hence this table is
+  // born. The idea is that
+  // reverseTable[device-storage:music-read] should return
+  // device-storage:music
+  let reverseTable = {};
+
+  for (let permName in PermissionsTable) {
+    let permAliases;
+    if (PermissionsTable[permName].access) {
+      permAliases = expandPermissions(permName, "readwrite");
+    } else {
+      permAliases = expandPermissions(permName);
+    }
+    for (let i = 0; i < permAliases.length; i++) {
+      reverseTable[permAliases[i]] = permName;
+    }
+  }
+
+  return reverseTable;
+
+})();
+
+this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
+
+  // Check to see if the 'webapp' is app/privileged/certified.
+  let appStatus;
+  switch (aIntStatus) {
+    case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
+      appStatus = "certified";
+      break;
+    case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
+      appStatus = "privileged";
+      break;
+    default: // If it isn't certified or privileged, it's app
+      appStatus = "app";
+      break;
+  }
+
+  let realPerm = PermissionsReverseTable[aPermName];
+
+  if (realPerm) {
+    return (PermissionsTable[realPerm][appStatus] == 
+            Ci.nsIPermissionManager.PROMPT_ACTION);
+  } else {
+    return false;
+  }
+}
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -863,25 +863,36 @@ this.DOMApplicationRegistry = {
       aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
       return;
     }
 
     Services.obs.notifyObservers(aMm, "webapps-launch", JSON.stringify(aData));
   },
 
   cancelDownload: function cancelDownload(aManifestURL) {
-    // We can't cancel appcache downloads for now.
-    if (!this.downloads[aManifestURL]) {
+    debug("cancelDownload " + aManifestURL);
+    let download = this.downloads[aManifestURL];
+    if (!download) {
+      debug("Could not find a download for " + aManifestURL);
       return;
     }
-    // This is a HTTP channel.
-    let download = this.downloads[aManifestURL]
-    download.channel.cancel(Cr.NS_BINDING_ABORTED);
+
+    if (download.cacheUpdate) {
+      // Cancel hosted app download.
+      try {
+        download.cacheUpdate.cancel();
+      } catch (e) { debug (e); }
+    } else if (download.channel) {
+      // Cancel packaged app download.
+      download.channel.cancel(Cr.NS_BINDING_ABORTED);
+    } else {
+      return;
+    }
+
     let app = this.webapps[download.appId];
-
     app.progress = 0;
     app.installState = download.previousState;
     app.downloading = false;
     app.downloadAvailable = false;
     app.downloadSize = 0;
     this._saveApps((function() {
       this.broadcastMessage("Webapps:PackageEvent",
                              { type: "canceled",
@@ -1033,36 +1044,52 @@ this.DOMApplicationRegistry = {
       });
     }).bind(this));
   },
 
   startOfflineCacheDownload: function startOfflineCacheDownload(aManifest, aApp,
                                                                 aProfileDir,
                                                                 aOfflineCacheObserver,
                                                                 aIsUpdate) {
-    // if the manifest has an appcache_path property, use it to populate the appcache
-    if (aManifest.appcache_path) {
-      let appcacheURI = Services.io.newURI(aManifest.fullAppcachePath(), null, null);
-      let docURI = Services.io.newURI(aManifest.fullLaunchPath(), null, null);
-      // We determine the app's 'installState' according to its previous
-      // state. Cancelled download should remain as 'pending'. Successfully
-      // installed apps should morph to 'updating'.
-      if (aIsUpdate) {
-        aApp.installState = "updating";
-      }
-      // We set the 'downloading' flag right before starting the app
-      // download/update.
-      aApp.downloading = true;
-      let cacheUpdate = aProfileDir
-        ? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
-        : updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
-      cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
-      if (aOfflineCacheObserver) {
-        cacheUpdate.addObserver(aOfflineCacheObserver, false);
-      }
+    if (!aManifest.appcache_path) {
+      return;
+    }
+
+    // If the manifest has an appcache_path property, use it to populate the
+    // appcache.
+    let appcacheURI = Services.io.newURI(aManifest.fullAppcachePath(),
+                                         null, null);
+    let docURI = Services.io.newURI(aManifest.fullLaunchPath(), null, null);
+
+    // We determine the app's 'installState' according to its previous
+    // state. Cancelled downloads should remain as 'pending'. Successfully
+    // installed apps should morph to 'updating'.
+    if (aIsUpdate) {
+      aApp.installState = "updating";
+    }
+
+    // We set the 'downloading' flag right before starting the app
+    // download/update.
+    aApp.downloading = true;
+    let cacheUpdate = aProfileDir
+      ? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
+      : updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
+
+    // We save the download details for potential further usage like cancelling
+    // it.
+    let download = {
+      cacheUpdate: cacheUpdate,
+      appId: this._appIdForManifestURL(aApp.manifestURL),
+      previousState: aIsUpdate ? "installed" : "pending"
+    };
+    this.downloads[aApp.manifestURL] = download;
+
+    cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
+    if (aOfflineCacheObserver) {
+      cacheUpdate.addObserver(aOfflineCacheObserver, false);
     }
   },
 
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
     let id = this._appIdForManifestURL(aData.manifestURL);
     let app = this.webapps[id];
 
@@ -1709,21 +1736,21 @@ this.DOMApplicationRegistry = {
                               app: app });
     }
 
     function download() {
       debug("About to download " + aManifest.fullPackagePath());
 
       let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath())
                                   .QueryInterface(Ci.nsIHttpChannel);
-      self.downloads[aApp.manifestURL] =
-        { channel:requestChannel,
-          appId: id,
-          previousState: aIsUpdate ? "installed" : "pending"
-        };
+      self.downloads[aApp.manifestURL] = {
+        channel: requestChannel,
+        appId: id,
+        previousState: aIsUpdate ? "installed" : "pending"
+      };
 
       let lastProgressTime = 0;
       requestChannel.notificationCallbacks = {
         QueryInterface: function notifQI(aIID) {
           if (aIID.equals(Ci.nsISupports)          ||
               aIID.equals(Ci.nsIProgressEventSink) ||
               aIID.equals(Ci.nsILoadContext))
             return this;
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1365,16 +1365,20 @@ public:
   const T& Value() const {
     return mImpl.ref();
   }
 
   T& Value() {
     return mImpl.ref();
   }
 
+  // If we ever decide to add conversion operators for optional arrays
+  // like the ones Nullable has, we'll need to ensure that Maybe<> has
+  // the boolean before the actual data.
+
 private:
   // Forbid copy-construction and assignment
   Optional(const Optional& other) MOZ_DELETE;
   const Optional &operator=(const Optional &other) MOZ_DELETE;
   
   Maybe<T> mImpl;
 };
 
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -3,36 +3,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Nullable_h
 #define mozilla_dom_Nullable_h
 
 #include "mozilla/Assertions.h"
+#include "nsTArrayForwardDeclare.h"
 
 namespace mozilla {
 namespace dom {
 
 // Support for nullable types
 template <typename T>
 struct Nullable
 {
 private:
+  // mIsNull MUST COME FIRST because otherwise the casting in our array
+  // conversion operators would shift where it is found in the struct.
+  bool mIsNull;
   T mValue;
-  bool mIsNull;
 
 public:
   Nullable()
     : mIsNull(true)
   {}
 
-  Nullable(T aValue)
-    : mValue(aValue)
-    , mIsNull(false)
+  explicit Nullable(T aValue)
+    : mIsNull(false)
+    , mValue(aValue)
   {}
 
   void SetValue(T aValue) {
     mValue = aValue;
     mIsNull = false;
   }
 
   // For cases when |T| is some type with nontrivial copy behavior, we may want
@@ -55,14 +58,31 @@ public:
   T& Value() {
     MOZ_ASSERT(!mIsNull);
     return mValue;
   }
 
   bool IsNull() const {
     return mIsNull;
   }
+
+  // Make it possible to use a const Nullable of an array type with other
+  // array types.
+  template<typename U>
+  operator const Nullable< nsTArray<U> >&() const {
+    // Make sure that T is ok to reinterpret to nsTArray<U>
+    const nsTArray<U>& arr = mValue;
+    (void)arr;
+    return *reinterpret_cast<const Nullable< nsTArray<U> >*>(this);
+  }
+  template<typename U>
+  operator const Nullable< FallibleTArray<U> >&() const {
+    // Make sure that T is ok to reinterpret to FallibleTArray<U>
+    const FallibleTArray<U>& arr = mValue;
+    (void)arr;
+    return *reinterpret_cast<const Nullable< FallibleTArray<U> >*>(this);
+  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_Nullable_h */
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -176,17 +176,17 @@ BluetoothDevice::Create(nsPIDOMWindow* a
 }
 
 void
 BluetoothDevice::Notify(const BluetoothSignal& aData)
 {
   if (aData.name().EqualsLiteral("PropertyChanged")) {
     NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue,
                  "PropertyChanged: Invalid value type");
-    InfallibleTArray<BluetoothNamedValue> arr = aData.value().get_ArrayOfBluetoothNamedValue();
+    InfallibleTArray<BluetoothNamedValue> arr(aData.value().get_ArrayOfBluetoothNamedValue());
 
     NS_ASSERTION(arr.Length() == 1, "Got more than one property in a change message!");
     BluetoothNamedValue v = arr[0];
     nsString name = v.name();
 
     SetPropertyByValue(v);
     if (name.EqualsLiteral("Connected")) {
       DispatchTrustedEvent(mConnected ? NS_LITERAL_STRING("connected")
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -698,17 +698,17 @@ BluetoothService::Observe(nsISupports* a
 
   MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 void
 BluetoothService::Notify(const BluetoothSignal& aData)
 {
-  InfallibleTArray<BluetoothNamedValue> arr = aData.value().get_ArrayOfBluetoothNamedValue();
+  InfallibleTArray<BluetoothNamedValue> arr(aData.value().get_ArrayOfBluetoothNamedValue());
   nsString type;
 
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   NS_ASSERTION(!::JS_IsExceptionPending(cx),
                "Shouldn't get here when an exception is pending!");
 
   JSAutoRequest jsar(cx);
   JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
--- a/dom/bluetooth/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth/ipc/BluetoothParent.cpp
@@ -388,19 +388,18 @@ BluetoothRequestParent::DoRequest(const 
 }
 
 bool
 BluetoothRequestParent::DoRequest(const DevicePropertiesRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TDevicePropertiesRequest);
 
-  // Have to copy because our array types don't match up
   nsresult rv =
-    mService->GetPairedDevicePropertiesInternal(nsTArray<nsString>(aRequest.addresses()),
+    mService->GetPairedDevicePropertiesInternal(aRequest.addresses(),
                                                 mReplyRunnable.get());
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 bool
 BluetoothRequestParent::DoRequest(const SetPinCodeRequest& aRequest)
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -1715,38 +1715,38 @@ public:
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsString devicePath;
     BluetoothValue v = mSignal.value();
     if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue &&
         v.get_ArrayOfBluetoothNamedValue().Length() ) {
-      InfallibleTArray<BluetoothNamedValue> arr = v.get_ArrayOfBluetoothNamedValue();
+      const InfallibleTArray<BluetoothNamedValue>& arr = v.get_ArrayOfBluetoothNamedValue();
       NS_ASSERTION(arr[0].value().type() == BluetoothValue::TnsString, "failed to get_nsString");
       devicePath = arr[0].value().get_nsString();
     }
     else if (v.type() == BluetoothValue::TnsString) {
       devicePath = v.get_nsString();
     }
     else {
       NS_WARNING("Invalid value type for GetDeviceProperties() method");
       return NS_ERROR_FAILURE;
     }
 
     BluetoothValue prop;
     if (!GetPropertiesInternal(devicePath, DBUS_DEVICE_IFACE, prop)) {
       NS_WARNING("Getting properties failed!");
       return NS_ERROR_FAILURE;
     }
-    InfallibleTArray<BluetoothNamedValue> properties = prop.get_ArrayOfBluetoothNamedValue();
+    InfallibleTArray<BluetoothNamedValue> properties(prop.get_ArrayOfBluetoothNamedValue());
     if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue) {
       // Return original dbus message parameters and also device name
       // for agent events "RequestConfirmation", "RequestPinCode", and "RequestPasskey"
-      InfallibleTArray<BluetoothNamedValue> parameters = v.get_ArrayOfBluetoothNamedValue();
+      InfallibleTArray<BluetoothNamedValue> parameters(v.get_ArrayOfBluetoothNamedValue());
 
       // For consistency, append path
       nsString path = parameters[0].value();
       BluetoothNamedValue pathprop;
       pathprop.name().AssignLiteral("Path");
       pathprop.value() = path;
       parameters.AppendElement(pathprop);
 
@@ -1799,17 +1799,17 @@ public:
   Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     DBusError err;
     dbus_error_init(&err);
 
     BluetoothValue values = InfallibleTArray<BluetoothNamedValue>();
 
-    for (int i = 0; i < mDeviceAddresses.Length(); i++) {
+    for (uint32_t i = 0; i < mDeviceAddresses.Length(); i++) {
       BluetoothValue v;
       if (!GetPropertiesInternal(mDeviceAddresses[i], DBUS_DEVICE_IFACE, v)) {
         nsAutoString errorStr;
         errorStr.AssignLiteral("Getting properties failed!");
         NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
         mRunnable->SetReply(new BluetoothReply(BluetoothReplyError(errorStr)));
         if (NS_FAILED(NS_DispatchToMainThread(mRunnable))) {
           NS_WARNING("Failed to dispatch to main thread!");
--- a/dom/browser-element/BrowserElementScrolling.js
+++ b/dom/browser-element/BrowserElementScrolling.js
@@ -1,207 +1,117 @@
 /* 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/. */
 
 const ContentPanning = {
   init: function cp_init() {
-    ['mousedown', 'mouseup', 'mousemove', 'touchstart', 'touchend', 'touchmove'].forEach(function(type) {
+    ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
       addEventListener(type, ContentPanning, false);
     });
 
     addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
     addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
   },
 
-  evtFilter: '',
-  _filterEvent: function cp_filterEvent(evt) {
-    switch (this.evtFilter) {
-      case 'mouse':
-        if (evt.type == 'touchstart' || evt.type == 'touchend' || evt.type == 'touchmove') {
-          return false;
-        }
-        break;
-      case 'touch':
-        if (evt.type == 'mousedown' || evt.type == 'mouseup' || evt.type == 'mousemove') {
-          return false;
-        }
-        break;
-    }
-    return true;
-  },
   handleEvent: function cp_handleEvent(evt) {
-    // determine scrolling detection is based on touch or mouse event at runtime
-    if (!this.evtFilter) {
-      if (evt.type == 'touchstart') this.evtFilter = 'touch';
-      else if (evt.type == 'mousedown') this.evtFilter = 'mouse';
-    }
-    if (evt.defaultPrevented || !this._filterEvent(evt)) return;
-
     switch (evt.type) {
       case 'mousedown':
-      case 'touchstart':
         this.onTouchStart(evt);
         break;
       case 'mousemove':
-      case 'touchmove':
         this.onTouchMove(evt);
         break;
       case 'mouseup':
-      case 'touchend':
         this.onTouchEnd(evt);
         break;
       case 'click':
         evt.stopPropagation();
         evt.preventDefault();
 
         let target = evt.target;
         let view = target.ownerDocument ? target.ownerDocument.defaultView
                                         : target;
         view.removeEventListener('click', this, true, true);
         break;
     }
   },
 
   position: new Point(0 , 0),
 
-  findFirstTouch: function cp_findFirstTouch(touches) {
-    if (!('trackingId' in this)) return undefined;
-
-    for (let i = 0; i < touches.length; i++) {
-      if (touches[i].identifier === this.trackingId)
-        return touches[i];
-    }
-    return undefined;
-  },
-
   onTouchStart: function cp_onTouchStart(evt) {
-    let target, screenX, screenY;
-    if (this.evtFilter == 'touch') {
-      if ('trackingId' in this) {
-        return;
-      }
-
-      let firstTouch = evt.changedTouches[0];
-      this.trackingId = firstTouch.identifier;
-      target = firstTouch.target;
-      screenX = firstTouch.screenX;
-      screenY = firstTouch.screenY;
-    } else {
-      target = evt.target;
-      screenX = evt.screenX;
-      screenY = evt.screenY;
-    }
-
     this.dragging = true;
     this.panning = false;
 
     let oldTarget = this.target;
-    [this.target, this.scrollCallback] = this.getPannable(target);
+    [this.target, this.scrollCallback] = this.getPannable(evt.target);
 
     // If we found a target, that means we have found a scrollable subframe. In
     // this case, and if we are using async panning and zooming on the parent
     // frame, inform the pan/zoom controller that it should not attempt to
     // handle any touch events it gets until the next batch (meaning the next
     // time we get a touch end).
     if (this.target != null && ContentPanning._asyncPanZoomForViewportFrame) {
       var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
-      os.notifyObservers(docShell, 'detect-scrollable-subframe', null);
+      os.notifyObservers(docShell, 'cancel-default-pan-zoom', null);
     }
 
     // If there is a pan animation running (from a previous pan gesture) and
     // the user touch back the screen, stop this animation immediatly and
     // prevent the possible click action if the touch happens on the same
     // target.
     this.preventNextClick = false;
     if (KineticPanning.active) {
       KineticPanning.stop();
 
       if (oldTarget && oldTarget == this.target)
         this.preventNextClick = true;
     }
 
 
-    this.position.set(screenX, screenY);
+    this.position.set(evt.screenX, evt.screenY);
     KineticPanning.record(new Point(0, 0), evt.timeStamp);
   },
 
   onTouchEnd: function cp_onTouchEnd(evt) {
-    if (this.evtFilter == 'touch' && !this.findFirstTouch(evt.changedTouches))
-      return;
-
     if (!this.dragging)
       return;
     this.dragging = false;
-    this.isScrolling = false;
 
     this.onTouchMove(evt);
 
-    delete this.trackingId;
-
-    let click = (this.evtFilter == 'touch') ? true : evt.detail;
+    let click = evt.detail;
     if (this.target && click && (this.panning || this.preventNextClick)) {
       let target = this.target;
       let view = target.ownerDocument ? target.ownerDocument.defaultView
                                       : target;
       view.addEventListener('click', this, true, true);
     }
 
     if (this.panning)
       KineticPanning.start(this);
   },
 
-  isScrolling: false, // Scrolling gesture is executed in BrowserElementScrolling
   onTouchMove: function cp_onTouchMove(evt) {
     if (!this.dragging || !this.scrollCallback)
       return;
 
-    let screenX, screenY;
-    if (this.evtFilter == 'touch') {
-      let firstTouch = this.findFirstTouch(evt.changedTouches);
-      if (evt.touches.length > 1 || !firstTouch)
-        return;
-      screenX = firstTouch.screenX;
-      screenY = firstTouch.screenY;
-    } else {
-      screenX = evt.screenX;
-      screenY = evt.screenY;
-    }