Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 18 Dec 2012 19:32:38 -0500
changeset 125530 287a7d7cf7f09fef3ddf43e39af48f9cb0a65593
parent 125484 9de611848111237f46893ccf838fdaa878374523 (current diff)
parent 125529 ba17dfaa86e991c6e5a7ec1ec6d8a5c77031bcd4 (diff)
child 125539 bfd85c9652fa63023a338c5d425d5ab319418d45
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [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 the last PGO-green inbound changeset to m-c.
content/media/test/short.mp4
image/public/imgIContainerObserver.idl
image/public/imgIDecoderObserver.idl
--- 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/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/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/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
@@ -158,17 +158,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.
--- 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/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/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/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());
 
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/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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9fafa32f93b2853972fc15aa954e4bface637feb
GIT binary patch
literal 67430
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
deleted file mode 100644
index 0b9870bdcb500389b78a84e7feb6cce31fd828b4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..51a23c5b490ac54af4eb7b9e66eacdc8c999d7ca
GIT binary patch
literal 2710
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
G+y4v4);q%h
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f9397a5106be1fe2b5838539096d43e5f0251f8d
GIT binary patch
literal 6825
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
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/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/browser-element/mochitest/Makefile.in
+++ b/dom/browser-element/mochitest/Makefile.in
@@ -159,41 +159,37 @@ MOCHITEST_FILES = \
 		browserElement_ReloadPostRequest.js \
 		browserElement_PurgeHistory.js \
 		test_browserElement_inproc_PurgeHistory.html \
 		$(NULL)
 
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 #		test_browserElement_inproc_Reload.html \
 
-# OOP tests don't work on Windows (bug 763081) or native-fennec (bug
-# 774939).
+# OOP tests don't work on native-fennec (bug 774939).
 #
 # Both the "inproc" and "oop" versions of OpenMixedProcess open remote frames,
 # so we don't run that test on platforms which don't support OOP tests.
 
-ifneq ($(OS_ARCH),WINNT) #{
 ifndef MOZ_ANDROID_OMTC #{
 MOCHITEST_FILES += \
 		browserElement_OpenMixedProcess.js \
 		file_browserElement_OpenMixedProcess.html \
 		test_browserElement_inproc_OpenMixedProcess.html \
 		test_browserElement_oop_OpenMixedProcess.html \
 		test_browserElement_oop_LoadEvents.html \
 		test_browserElement_oop_DataURI.html \
 		test_browserElement_oop_ErrorSecurity.html \
 		test_browserElement_oop_Titlechange.html \
 		test_browserElement_oop_AppWindowNamespace.html \
 		test_browserElement_oop_BrowserWindowNamespace.html \
 		test_browserElement_oop_TopBarrier.html \
 		test_browserElement_oop_Iconchange.html \
 		test_browserElement_oop_GetScreenshot.html \
 		test_browserElement_oop_BadScreenshot.html \
-		test_browserElement_oop_SetVisibleFrames.html \
-		test_browserElement_oop_SetVisibleFrames2.html \
 		test_browserElement_oop_KeyEvents.html \
 		test_browserElement_oop_XFrameOptions.html \
 		test_browserElement_oop_XFrameOptionsDeny.html \
 		test_browserElement_oop_XFrameOptionsSameOrigin.html \
 		test_browserElement_oop_XFrameOptionsAllowFrom.html \
 		test_browserElement_oop_FirstPaint.html \
 		test_browserElement_oop_Alert.html \
 		test_browserElement_oop_AlertInFrame.html \
@@ -226,18 +222,20 @@ MOCHITEST_FILES += \
 		test_browserElement_oop_FrameWrongURI.html \
 		test_browserElement_oop_ReloadPostRequest.html \
 		test_browserElement_oop_PurgeHistory.html \
 	$(NULL)
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 MOCHITEST_FILES += \
 		test_browserElement_oop_SetVisible.html \
+		test_browserElement_oop_SetVisibleFrames.html \
+		test_browserElement_oop_SetVisibleFrames2.html \
 		$(NULL)
 else
 $(filter disabled-temporarily--bug-779753, test_browserElement_oop_SetVisible.html)
+$(filter disabled-temporarily--bug-779753, test_browserElement_oop_SetVisibleFrames.html)
+$(filter disabled-temporarily--bug-779753, test_browserElement_oop_SetVisibleFrames2.html)
 endif
 
 endif #}
-endif #}
-
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/interfaces/permission/nsIDOMPermissionSettings.idl
+++ b/dom/interfaces/permission/nsIDOMPermissionSettings.idl
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIDOMDOMRequest;
 
-[scriptable, uuid(18390770-02ab-11e2-a21f-0800200c9a66)]
+[scriptable, uuid(b3e3894e-b24e-4174-9c80-08115709615b)]
 interface nsIDOMPermissionSettings : nsISupports
 {
   DOMString get(in DOMString permission, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
 
   void set(in DOMString permission, in DOMString value, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
+
+  bool isExplicit(in DOMString permission, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
 };
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -8,16 +8,22 @@
 
 #include "ContentProcess.h"
 
 using mozilla::ipc::IOThreadChild;
 
 namespace mozilla {
 namespace dom {
 
+void
+ContentProcess::SetAppDir(const nsACString& aPath)
+{
+  mXREEmbed.SetAppDir(aPath);
+}
+
 bool
 ContentProcess::Init()
 {
     mContent.Init(IOThreadChild::message_loop(),
                          ParentHandle(),
                          IOThreadChild::channel());
     mXREEmbed.Start();
     mContent.InitXPCOM();
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -31,16 +31,18 @@ public:
     { }
 
     ~ContentProcess()
     { }
 
     virtual bool Init() MOZ_OVERRIDE;
     virtual void CleanUp() MOZ_OVERRIDE;
 
+    void SetAppDir(const nsACString& aPath);
+
 private:
     ContentChild mContent;
     mozilla::ipc::ScopedXREEmbed mXREEmbed;
 
     DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
 }  // namespace dom
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -198,16 +198,23 @@ MediaDevice::GetName(nsAString& aName)
 
 NS_IMETHODIMP
 MediaDevice::GetType(nsAString& aType)
 {
   aType.Assign(mType);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaDevice::GetId(nsAString& aID)
+{
+  aID.Assign(mID);
+  return NS_OK;
+}
+
 MediaEngineSource*
 MediaDevice::GetSource()
 {
   return mSource;
 }
 
 /**
  * Creates a MediaStream, attaches a listener and fires off a success callback
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -249,28 +249,31 @@ class MediaDevice : public nsIMediaDevic
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEDIADEVICE
 
   MediaDevice(MediaEngineVideoSource* aSource) {
     mSource = aSource;
     mType.Assign(NS_LITERAL_STRING("video"));
     mSource->GetName(mName);
+    mSource->GetUUID(mID);
   }
   MediaDevice(MediaEngineAudioSource* aSource) {
     mSource = aSource;
     mType.Assign(NS_LITERAL_STRING("audio"));
     mSource->GetName(mName);
+    mSource->GetUUID(mID);
   }
   virtual ~MediaDevice() {}
 
   MediaEngineSource* GetSource();
 private:
   nsString mName;
   nsString mType;
+  nsString mID;
   nsRefPtr<MediaEngineSource> mSource;
 };
 
 class MediaManager MOZ_FINAL : public nsIObserver
 {
 public:
   static MediaManager* Get() {
     if (!sSingleton) {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -128,17 +128,18 @@ IceCandidate.prototype = {
   constructor: function(win, candidateInitDict) {
     if (this._win) {
       throw new Error("Constructor already called");
     }
     this._win = win;
     if (candidateInitDict !== undefined) {
       this.candidate = candidateInitDict.candidate || null;
       this.sdpMid = candidateInitDict.sdbMid || null;
-      this.sdpMLineIndex = candidateInitDict.sdpMLineIndex || null;
+      this.sdpMLineIndex = candidateInitDict.sdpMLineIndex === null ?
+            null : candidateInitDict.sdpMLineIndex + 1;
     } else {
       this.candidate = this.sdpMid = this.sdpMLineIndex = null;
     }
   }
 };
 
 function SessionDescription(type, sdp) {
   this.type = type;
--- a/dom/media/nsIDOMNavigatorUserMedia.idl
+++ b/dom/media/nsIDOMNavigatorUserMedia.idl
@@ -6,16 +6,17 @@
 #include "nsIVariant.idl"
 #include "nsIDOMMediaStream.idl"
 
 [scriptable, builtinclass, uuid(6de854f9-acf8-4383-b464-4803631ef309)]
 interface nsIMediaDevice : nsISupports
 {
   readonly attribute DOMString type;
   readonly attribute DOMString name;
+  readonly attribute DOMString id;
 };
 
 [scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
 interface nsIGetUserMediaDevicesSuccessCallback : nsISupports
 {
   void onSuccess(in nsIVariant devices);
 };
 
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -6,16 +6,17 @@
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 Cu.import("resource://gre/modules/PermissionSettings.jsm");
 
 this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
                          "SystemMessagePermissionsTable"];
 
 function debug(aStr) {
   // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
 }
@@ -74,16 +75,19 @@ this.SystemMessagePermissionsTable = {
     "desktop-notification": []
   },
   "sms-received": {
     "sms": []
   },
   "telephony-new-call": {
     "telephony": []
   },
+  "telephony-call-ended": {
+    "telephony": []
+  },
   "ussd-received": {
     "mobileconnection": []
   }
 };
 
 this.SystemMessagePermissionsChecker = {
   /**
    * Return all the needed permission names for the given system message.
--- a/dom/permission/PermissionPromptHelper.jsm
+++ b/dom/permission/PermissionPromptHelper.jsm
@@ -30,16 +30,17 @@ const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["PermissionPromptHelper"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "permissionPromptService",
                                    "@mozilla.org/permission-prompt-service;1",
                                    "nsIPermissionPromptService");
--- a/dom/permission/PermissionSettings.js
+++ b/dom/permission/PermissionSettings.js
@@ -9,16 +9,17 @@ function debug(aMsg) {
 }
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
 // PermissionSettings
 
 const PERMISSIONSETTINGS_CONTRACTID = "@mozilla.org/permissionSettings;1";
 const PERMISSIONSETTINGS_CID        = Components.ID("{18390770-02ab-11e2-a21f-0800200c9a66}");
 const nsIDOMPermissionSettings      = Ci.nsIDOMPermissionSettings;
@@ -62,19 +63,41 @@ PermissionSettings.prototype = {
       case Ci.nsIPermissionManager.PROMPT_ACTION:
         return "prompt";
       default:
         dump("Unsupported PermissionSettings Action!\n");
         return "unknown";
     }
   },
 
-  set: function set(aPermName, aPermValue, aManifestURL, aOrigin, aBrowserFlag) {
-    debug("Set called with: " + aPermName + ", " + aManifestURL + ", " + aOrigin + ",  " + aPermValue + ", " + aBrowserFlag);
+  isExplicit: function isExplicit(aPermName, aManifestURL, aOrigin,
+                                  aBrowserFlag) {
+    debug("isExplicit: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
+    let uri = Services.io.newURI(aOrigin, null, null);
+    let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
+    let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
+
+    return isExplicitInPermissionsTable(aPermName, principal.appStatus);
+  },
+
+  set: function set(aPermName, aPermValue, aManifestURL, aOrigin,
+                    aBrowserFlag) {
+    debug("Set called with: " + aPermName + ", " + aManifestURL + ", " +
+          aOrigin + ",  " + aPermValue + ", " + aBrowserFlag); 
     let action;
+    // Check for invalid calls so that we throw an exception rather than get
+    // killed by parent process
+    if (aPermValue === "unknown" ||
+        !this.isExplicit(aPermName, aManifestURL, aOrigin, aBrowserFlag)) {
+      let errorMsg = "PermissionSettings.js: '" + aPermName + "'" +
+                     " is an implicit permission for '" + aManifestURL+"'";
+      Cu.reportError(errorMsg);
+      throw new Components.Exception(errorMsg);
+    }
+
     cpm.sendSyncMessage("PermissionSettings:AddPermission", {
       type: aPermName,
       origin: aOrigin,
       manifestURL: aManifestURL,
       value: aPermValue,
       browserFlag: aBrowserFlag
     });
   },
--- a/dom/permission/PermissionSettings.jsm
+++ b/dom/permission/PermissionSettings.jsm
@@ -11,16 +11,17 @@ function debug(s) {
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["PermissionSettingsModule"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "permissionManager",
                                    "@mozilla.org/permissionmanager;1",
@@ -38,17 +39,46 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 this.PermissionSettingsModule = {
   init: function init() {
     debug("Init");
     ppmm.addMessageListener("PermissionSettings:AddPermission", this);
     Services.obs.addObserver(this, "profile-before-change", false);
   },
 
+
+  _isChangeAllowed: function(aPrincipal, aPermName, aAction) {
+    // Bug 812289:
+    // Change is allowed from a child process when all of the following
+    // conditions stand true:
+    //   * the action isn't "unknown" (so the change isn't a delete)
+    //   * the permission already exists on the database
+    //   * the permission is marked as explicit on the permissions table
+    // Note that we *have* to check the first two conditions ere because
+    // permissionManager doesn't know if it's being called as a result of
+    // a parent process or child process request. We could check
+    // if the permission is actually explicit (and thus modifiable) or not
+    // on permissionManager also but we currently don't.
+    let perm =
+      permissionManager.testExactPermissionFromPrincipal(aPrincipal,aPermName);
+    let isExplicit = isExplicitInPermissionsTable(aPermName, aPrincipal.appStatus);
+    
+    return (aAction !== "unknown") &&
+           (perm !== Ci.nsIPermissionManager.UNKNOWN_ACTION) &&
+           isExplicit;
+  },
+
   addPermission: function addPermission(aData, aCallbacks) {
+
+    this._internalAddPermission(aData, true, aCallbacks);
+
+  },
+
+
+  _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) {
     let uri = Services.io.newURI(aData.origin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aData.manifestURL);
     let principal = secMan.getAppCodebasePrincipal(uri, appID, aData.browserFlag);
 
     let action;
     switch (aData.value)
     {
       case "unknown":
@@ -62,18 +92,26 @@ this.PermissionSettingsModule = {
         break;
       case "prompt":
         action = Ci.nsIPermissionManager.PROMPT_ACTION;
         break;
       default:
         dump("Unsupported PermisionSettings Action: " + aData.value +"\n");
         action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
     }
-    debug("add: " + aData.origin + " " + appID + " " + action);
-    permissionManager.addFromPrincipal(principal, aData.type, action);
+
+    if (aAllowAllChanges ||
+        this._isChangeAllowed(principal, aData.type, aData.value)) {
+      debug("add: " + aData.origin + " " + appID + " " + action);
+      permissionManager.addFromPrincipal(principal, aData.type, action);
+      return true;
+    } else {
+      debug("add Failure: " + aData.origin + " " + appID + " " + action);
+      return false; // This isn't currently used, see comment on setPermission
+    }
   },
 
   getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
     debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
     let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
     let result = permissionManager.testExactPermissionFromPrincipal(principal, aPermName);
@@ -103,20 +141,30 @@ this.PermissionSettingsModule = {
   receiveMessage: function receiveMessage(aMessage) {
     debug("PermissionSettings::receiveMessage " + aMessage.name);
     let mm = aMessage.target;
     let msg = aMessage.data;
 
     let result;
     switch (aMessage.name) {
       case "PermissionSettings:AddPermission":
-        if (!aMessage.target.assertPermission("permissions")) {
-          Cu.reportError("PermissionSettings message " + msg.name +
-                         " from a content process with no 'permissions' privileges.");
+        let success = false;
+        let errorMsg = 
+              " from a content process with no 'permissions' privileges.";
+        if (mm.assertPermission("permissions")) {
+          success = this._internalAddPermission(msg, false);
+          if (!success) { 
+            // Just kill the calling process
+            mm.assertPermission("permissions-modify-implicit");
+            errorMsg = " had an implicit permission change. Child process killed.";
+          }
+        }
+
+        if (!success) {
+          Cu.reportError("PermissionSettings message " + msg.name + errorMsg);
           return null;
         }
-        this.addPermission(msg);
         break;
     }
   }
 }
 
 PermissionSettingsModule.init();
--- a/dom/permission/tests/test_permission_basics.html
+++ b/dom/permission/tests/test_permission_basics.html
@@ -16,45 +16,83 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 "use strict";
 
+var testPrivApp = {
+  'manifestURL' : 'https://aprivileged.com/manifest.webapp'
+};
+
+var testCertApp = {
+  'manifestURL' : 'https://acertified.com/manifest.webapp'
+};
+
 SpecialPowers.addPermission("permissions", true, document);
 var comp = SpecialPowers.wrap(Components);
 SpecialPowers.pushPrefEnv({ "set": [["dom.mozPermissionSettings.enabled", true]] }, 
                           function() {
                             SpecialPowers.removePermission("permissions", document);
                           });
 
 comp.utils.import("resource://gre/modules/PermissionSettings.jsm");
 var mozPermissions = window.navigator.mozPermissionSettings;
 
 function permissionTest() {
+  // Any permission explicit for privileged and implicit for certified serves
+  var testPerm = "geolocation";
+
+  // Simulate than the app request the permissions
+  SpecialPowers.addPermission(testPerm, true, testPrivApp);
+  SpecialPowers.addPermission(testPerm, true, testCertApp);
+
   if (gPermissionssEnabled) {
-    mozPermissions.set("a", "unknown", "http://examplemanifestURI.com", "http://origin.com", true);
-    var result = mozPermissions.get("a", "http://examplemanifestURI.com", "http://origin.com", true);
-    is(result, "unknown", "same result");
-    mozPermissions.set("a", "allow", "http://examplemanifestURI.com", "http://origin.com", true);
-    result = mozPermissions.get("a", "http://examplemanifestURI.com", "http://origin.com", true);
+    var certAppManifest = testCertApp.manifestURL;
+    var privAppManifest = testPrivApp.manifestURL;
+    var originPriv = "https://aprivileged.com";
+    var originCert = "https://acertified.com";
+
+    // Trying to make any change to implicit permissions should fail
+    try {
+      mozPermissions.set(testPerm, "allow", certAppManifest, originCert, false);
+      ok(false, "Change implicit permission");
+    } catch (e) {
+      ok(true, "Change implicit permission");
+    }
+
+    var result=mozPermissions.get(testPerm, certAppManifest, originCert, false);
     is(result, "allow", "same result");
-    mozPermissions.set("a", "deny", "http://examplemanifestURI.com", "http://origin.com", true);
-    result = mozPermissions.get("a", "http://examplemanifestURI.com", "http://origin.com", true);
-    is(result, "deny", "same result");
-    mozPermissions.set("a", "prompt", "http://examplemanifestURI.com", "http://origin.com", true);
-    result = mozPermissions.get("a", "http://examplemanifestURI.com", "http://origin.com", true);
-    is(result, "prompt", "same result");
+
+    // Erasing a permission, even an explicit one, is not allowed
+    try {
+      mozPermissions.set(testPerm, "unknown", privAppManifest, originPriv, false);
+      ok(false, "Erase explicit permission");
+    } catch (e) {
+      ok(true, "Erase explicit permission");
+    }
+
+    mozPermissions.set(testPerm, "allow", privAppManifest, originPriv, false);
+    result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
+    is(result, "allow", "Set to allow");
+    mozPermissions.set(testPerm, "deny", privAppManifest, originPriv, false);
+    result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
+    is(result, "deny", "Set to deny");
+    mozPermissions.set(testPerm, "prompt", privAppManifest, originPriv, false);
+    result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
+    is(result, "prompt", "Set to prompt");
     SimpleTest.finish();
   } else {
     is(mozPermissions, null, "mozPermissionSettings is null when not enabled.");
     SimpleTest.finish();
   }
+  SpecialPowers.removePermission(testPerm, testPrivApp);
+  SpecialPowers.removePermission(testPerm, testCertApp);
 }
 
 var gPermissionssEnabled = SpecialPowers.getBoolPref("dom.mozPermissionSettings.enabled");
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(permissionTest);
 
 ok(true, "test passed");
 </script>
--- a/dom/permission/tests/unit/test_bug808734.js
+++ b/dom/permission/tests/unit/test_bug808734.js
@@ -46,16 +46,16 @@ function do_check_set_eq(a1, a2) {
 
   for (let i = 0; i < a1.length; ++i) {
     do_check_eq(a1[i], a2[i])
   }
 }
 
 function run_test() {
   var scope = {};
-  Cu.import("resource://gre/modules/PermissionsInstaller.jsm", scope);
+  Cu.import("resource://gre/modules/PermissionsTable.jsm", scope);
 
   for (var i = 0; i < gData.length; i++) {
     var perms = scope.expandPermissions(gData[i].permission,
                                         gData[i].access);
     do_check_set_eq(perms, gData[i].expected);
   }
 }
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1237,16 +1237,24 @@ RadioInterfaceLayer.prototype = {
   },
 
   /**
    * Handle call disconnects by updating our current state and the audio system.
    */
   handleCallDisconnected: function handleCallDisconnected(call) {
     debug("handleCallDisconnected: " + JSON.stringify(call));
     call.state = nsIRadioInterfaceLayer.CALL_STATE_DISCONNECTED;
+    let duration = ("started" in call && typeof call.started == "number") ?
+      new Date().getTime() - call.started : 0;
+    let data = {
+      number: call.number,
+      duration: duration,
+      direction: call.direction
+    };
+    gSystemMessenger.broadcastMessage("telephony-call-ended", data);
     this.updateCallAudioState(call);
     this._sendTargetMessage("telephony", "RIL:CallStateChanged", call);
   },
 
   /**
    * Handle calls delivered in response to a 'enumerateCalls' request.
    */
   handleEnumerateCalls: function handleEnumerateCalls(options) {
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -4297,16 +4297,19 @@ let RIL = {
         newCall = newCalls[currentCall.callIndex];
         delete newCalls[currentCall.callIndex];
       }
 
       if (newCall) {
         // Call is still valid.
         if (newCall.state != currentCall.state) {
           // State has changed.
+          if (!currentCall.started && newCall.state == CALL_STATE_ACTIVE) {
+            currentCall.started = new Date().getTime();
+          }
           currentCall.state = newCall.state;
           this._handleChangedCallState(currentCall);
         }
       } else {
         // Call is no longer reported by the radio. Remove from our map and
         // send disconnected state change.
         delete this.currentCalls[currentCall.callIndex];
         this.getFailCauseCode(currentCall);
@@ -4317,16 +4320,21 @@ let RIL = {
     for each (let newCall in newCalls) {
       if (newCall.isVoice) {
         // Format international numbers appropriately.
         if (newCall.number &&
             newCall.toa == TOA_INTERNATIONAL &&
             newCall.number[0] != "+") {
           newCall.number = "+" + newCall.number;
         }
+        if (newCall.state == CALL_STATE_INCOMING) {
+          newCall.direction = 'incoming';
+        } else if (newCall.state == CALL_STATE_DIALING) {
+          newCall.direction = 'outgoing';
+        }
         // Add to our map.
         this.currentCalls[newCall.callIndex] = newCall;
         this._handleChangedCallState(newCall);
       }
     }
 
     // Update our mute status. If there is anything in our currentCalls map then
     // we know it's a voice call and we should leave audio on.
--- a/gfx/layers/LayerSorter.cpp
+++ b/gfx/layers/LayerSorter.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LayerSorter.h"
 #include "DirectedGraph.h"
 #include "limits.h"
 #include "gfxLineSegment.h"
 #include "Layers.h"
+#include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace layers {
 
 enum LayerSortOrder {
   Undefined,
   ABeforeB,
   BBeforeA,
@@ -273,16 +274,17 @@ void SortLayersBy3DZOrder(nsTArray<Layer
 
   // Move each item without incoming edges into the sorted list,
   // and remove edges from it.
   do {
     if (!noIncoming.IsEmpty()) {
       uint32_t last = noIncoming.Length() - 1;
 
       Layer* layer = noIncoming.ElementAt(last);
+      MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList
 
       noIncoming.RemoveElementAt(last);
       sortedList.AppendElement(layer);
 
       nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
       graph.GetEdgesFrom(layer, outgoing);
       for (uint32_t i = 0; i < outgoing.Length(); i++) {
         DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
@@ -306,19 +308,21 @@ void SortLayersBy3DZOrder(nsTArray<Layer
           minEdges = edgeCount;
           minNode = aLayers.ElementAt(i);
           if (minEdges == 1) {
             break;
           }
         }
       }
 
-      // Remove all of them!
-      graph.RemoveEdgesTo(minNode);
-      noIncoming.AppendElement(minNode);
+      if (minNode) {
+        // Remove all of them!
+        graph.RemoveEdgesTo(minNode);
+        noIncoming.AppendElement(minNode);
+      }
     }
   } while (!noIncoming.IsEmpty());
   NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
 #ifdef DEBUG
   if (gDumpLayerSortList) {
     fprintf(stderr, " --- Layers after sorting: --- \n");
     DumpLayerList(sortedList);
   }
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1076,32 +1076,33 @@ gfxFT2FontList::FindFonts()
     FindFontsInDir(root, &fnc);
 
     if (mFontFamilies.Count() == 0) {
         // if we can't find/read the font directory, we are doomed!
         NS_RUNTIMEABORT("Could not read the system fonts directory");
     }
 
     // look for fonts shipped with the product
+    NS_NAMED_LITERAL_STRING(kFontsDirName, "fonts");
     nsCOMPtr<nsIFile> localDir;
     nsresult rv = NS_GetSpecialDirectory(NS_APP_RES_DIR,
                                          getter_AddRefs(localDir));
-    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
+    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(kFontsDirName))) {
         ExtractFontsFromJar(localDir);
         nsCString localPath;
         rv = localDir->GetNativePath(localPath);
         if (NS_SUCCEEDED(rv)) {
             FindFontsInDir(localPath, &fnc);
         }
     }
 
-    // look for locally-added fonts in the profile
+    // look for locally-added fonts in a "fonts" subdir of the profile
     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                                 getter_AddRefs(localDir));
-    if (NS_SUCCEEDED(rv)) {
+    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(kFontsDirName))) {
         nsCString localPath;
         rv = localDir->GetNativePath(localPath);
         if (NS_SUCCEEDED(rv)) {
             FindFontsInDir(localPath, &fnc);
         }
     }
 
     // Finalize the families by sorting faces into standard order
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -10,17 +10,16 @@
 #include <stdlib.h>
 
 #include "ImageLogging.h"
 #include "EndianMacros.h"
 #include "nsBMPDecoder.h"
 
 #include "nsIInputStream.h"
 #include "RasterImage.h"
-#include "imgIContainerObserver.h"
 
 namespace mozilla {
 namespace image {
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *
 GetBMPLog()
 {
@@ -30,17 +29,17 @@ GetBMPLog()
   return sBMPLog;
 }
 #endif
 
 // Convert from row (1..height) to absolute line (0..height-1)
 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
 
-nsBMPDecoder::nsBMPDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
+nsBMPDecoder::nsBMPDecoder(RasterImage &aImage, imgDecoderObserver* aObserver)
  : Decoder(aImage, aObserver)
 {
   mColors = nullptr;
   mRow = nullptr;
   mImageData = nullptr;
   mCurPos = mPos = mNumColors = mRowBytes = 0;
   mOldLine = mCurLine = 1; // Otherwise decoder will never start
   mState = eRLEStateInitial;
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -3,34 +3,34 @@
  * 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 _nsBMPDecoder_h
 #define _nsBMPDecoder_h
 
 #include "nsAutoPtr.h"
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 #include "gfxColor.h"
 #include "Decoder.h"
 #include "BMPFileHeaders.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
 /**
  * Decoder for BMP-Files, as used by Windows and OS/2
  */
 class nsBMPDecoder : public Decoder
 {
 public:
 
-    nsBMPDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver);
+    nsBMPDecoder(RasterImage &aImage, imgDecoderObserver* aObserver);
     ~nsBMPDecoder();
 
     // Specifies whether or not the BMP file will contain alpha data
     // If set to true and the BMP is 32BPP, the alpha data will be
     // retrieved from the 4th byte of image data per pixel 
     void SetUseAlphaData(bool useAlphaData);
     // Obtains the bits per pixel from the internal BIH header
     int32_t GetBitsPerPixel() const;
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -37,17 +37,16 @@ organizations who wish to receive copies
 or revised. This service is offered free of charge; please provide us with your
 mailing address.
 */
 
 #include <stddef.h>
 
 #include "nsGIFDecoder2.h"
 #include "nsIInputStream.h"
-#include "imgIContainerObserver.h"
 #include "RasterImage.h"
 
 #include "gfxColor.h"
 #include "gfxPlatform.h"
 #include "qcms.h"
 
 namespace mozilla {
 namespace image {
@@ -67,17 +66,17 @@ namespace image {
     mGIFStruct.state = (s);            \
   PR_END_MACRO
 
 /* Get a 16-bit value stored in little-endian format */
 #define GETINT16(p)   ((p)[1]<<8|(p)[0])
 //////////////////////////////////////////////////////////////////////
 // GIF Decoder Implementation
 
-nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage, imgIDecoderObserver* aObserver)
+nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage, imgDecoderObserver* aObserver)
   : Decoder(aImage, aObserver)
   , mCurrentRow(-1)
   , mLastFlushedRow(-1)
   , mImageData(nullptr)
   , mColormap(nullptr)
   , mOldColor(0)
   , mCurrentFrame(-1)
   , mCurrentPass(0)
@@ -214,19 +213,17 @@ nsresult nsGIFDecoder2::BeginImageFrame(
   // Tell the superclass we're starting a frame
   PostFrameStart();
 
   if (!mGIFStruct.images_decoded) {
     // Send a onetime invalidation for the first frame if it has a y-axis offset. 
     // Otherwise, the area may never be refreshed and the placeholder will remain
     // on the screen. (Bug 37589)
     if (mGIFStruct.y_offset > 0) {
-      int32_t imgWidth;
-      mImage.GetWidth(&imgWidth);
-      nsIntRect r(0, 0, imgWidth, mGIFStruct.y_offset);
+      nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
       PostInvalidation(r);
     }
   }
 
   mCurrentFrame = mGIFStruct.images_decoded;
   return NS_OK;
 }
 
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -4,32 +4,32 @@
  * 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 _nsGIFDecoder2_h
 #define _nsGIFDecoder2_h
 
 #include "nsCOMPtr.h"
 #include "Decoder.h"
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 
 #include "GIF2.h"
 
 namespace mozilla {
 namespace image {
 class RasterImage;
 
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
 class nsGIFDecoder2 : public Decoder
 {
 public:
 
-  nsGIFDecoder2(RasterImage &aImage, imgIDecoderObserver* aObserver);
+  nsGIFDecoder2(RasterImage &aImage, imgDecoderObserver* aObserver);
   ~nsGIFDecoder2();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
   virtual void FinishInternal();
   virtual Telemetry::ID SpeedHistogram();
 
 private:
   /* These functions will be called when the decoder has a decoded row,
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -9,17 +9,16 @@
 #include <stdlib.h>
 
 #include "EndianMacros.h"
 #include "nsICODecoder.h"
 
 #include "nsIInputStream.h"
 #include "nsIComponentManager.h"
 #include "RasterImage.h"
-#include "imgIContainerObserver.h"
 
 #include "nsIProperties.h"
 #include "nsISupportsPrimitives.h"
 
 namespace mozilla {
 namespace image {
 
 #define ICONCOUNTOFFSET 4
@@ -58,17 +57,17 @@ nsICODecoder::GetNumColors()
     default:
       numColors = (uint16_t)-1;
     }
   }
   return numColors;
 }
 
 
-nsICODecoder::nsICODecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
+nsICODecoder::nsICODecoder(RasterImage &aImage, imgDecoderObserver* aObserver)
  : Decoder(aImage, aObserver)
 {
   mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0;
   mIsPNG = false;
   mRow = nullptr;
   mOldLine = mCurLine = 1; // Otherwise decoder will never start
 }
 
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -4,31 +4,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef _nsICODecoder_h
 #define _nsICODecoder_h
 
 #include "nsAutoPtr.h"
 #include "Decoder.h"
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 #include "nsBMPDecoder.h"
 #include "nsPNGDecoder.h"
 #include "ICOFileHeaders.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
 class nsICODecoder : public Decoder
 {
 public:
 
-  nsICODecoder(RasterImage &aImage, imgIDecoderObserver* aObserver);
+  nsICODecoder(RasterImage &aImage, imgDecoderObserver* aObserver);
   virtual ~nsICODecoder();
 
   // Obtains the width of the icon directory entry
   uint32_t GetRealWidth() const
   {
     return mDirEntry.mWidth == 0 ? 256 : mDirEntry.mWidth; 
   }
 
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -2,26 +2,25 @@
  *
  * 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 "nsIconDecoder.h"
 #include "nsIInputStream.h"
 #include "RasterImage.h"
-#include "imgIContainerObserver.h"
 #include "nspr.h"
 #include "nsRect.h"
 
 #include "nsError.h"
 
 namespace mozilla {
 namespace image {
 
-nsIconDecoder::nsIconDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
+nsIconDecoder::nsIconDecoder(RasterImage &aImage, imgDecoderObserver* aObserver)
  : Decoder(aImage, aObserver),
    mWidth(-1),
    mHeight(-1),
    mPixBytesRead(0),
    mPixBytesTotal(0),
    mImageData(nullptr),
    mState(iconStateStart)
 {
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -6,17 +6,17 @@
 
 #ifndef nsIconDecoder_h__
 #define nsIconDecoder_h__
 
 #include "Decoder.h"
 
 #include "nsCOMPtr.h"
 
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 
 namespace mozilla {
 namespace image {
 class RasterImage;
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 // The icon decoder is a decoder specifically tailored for loading icons 
 // from the OS. We've defined our own little format to represent these icons
@@ -35,17 +35,17 @@ class RasterImage;
 //
 //
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 class nsIconDecoder : public Decoder
 {
 public:
 
-  nsIconDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver);
+  nsIconDecoder(RasterImage &aImage, imgDecoderObserver* aObserver);
   virtual ~nsIconDecoder();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
 
   uint8_t mWidth;
   uint8_t mHeight;
   uint32_t mPixBytesRead;
   uint32_t mPixBytesTotal;
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -2,18 +2,16 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageLogging.h"
 #include "nsJPEGDecoder.h"
 
-#include "imgIContainerObserver.h"
-
 #include "nsIInputStream.h"
 
 #include "nspr.h"
 #include "nsCRT.h"
 #include "gfxColor.h"
 
 #include "jerror.h"
 
@@ -77,17 +75,17 @@ METHODDEF(boolean) fill_input_buffer (j_
 METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
 METHODDEF(void) term_source (j_decompress_ptr jd);
 METHODDEF(void) my_error_exit (j_common_ptr cinfo);
 
 /* Normal JFIF markers can't have more bytes than this. */
 #define MAX_JPEG_MARKER_LENGTH  (((uint32_t)1 << 16) - 1)
 
 
-nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage, imgIDecoderObserver* aObserver, Decoder::DecodeStyle aDecodeStyle)
+nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage, imgDecoderObserver* aObserver, Decoder::DecodeStyle aDecodeStyle)
  : Decoder(aImage, aObserver)
  , mDecodeStyle(aDecodeStyle)
 {
   mState = JPEG_HEADER;
   mReading = true;
   mImageData = nullptr;
 
   mBytesToSkip = 0;
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -12,17 +12,17 @@
  * But the jpeg decoder has its own definition of INT32. To avoid build issues,
  * we need to undefine the version from 'windows.h'. */
 #undef INT32
 
 #include "Decoder.h"
 
 #include "nsAutoPtr.h"
 
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 #include "nsIInputStream.h"
 #include "nsIPipe.h"
 #include "qcms.h"
 
 extern "C" {
 #include "jpeglib.h"
 }
 
@@ -47,17 +47,17 @@ typedef enum {
     JPEG_ERROR    
 } jstate;
 
 class RasterImage;
 
 class nsJPEGDecoder : public Decoder
 {
 public:
-  nsJPEGDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver, Decoder::DecodeStyle aDecodeStyle);
+  nsJPEGDecoder(RasterImage &aImage, imgDecoderObserver* aObserver, Decoder::DecodeStyle aDecodeStyle);
   virtual ~nsJPEGDecoder();
 
   virtual void InitInternal();
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
   virtual void FinishInternal();
 
   virtual Telemetry::ID SpeedHistogram();
   void NotifyDone();
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -8,17 +8,16 @@
 #include "nsPNGDecoder.h"
 
 #include "nsMemory.h"
 #include "nsRect.h"
 
 #include "nsIInputStream.h"
 
 #include "RasterImage.h"
-#include "imgIContainerObserver.h"
 
 #include "gfxColor.h"
 #include "nsColor.h"
 
 #include "nspr.h"
 #include "png.h"
 
 #include "gfxPlatform.h"
@@ -53,17 +52,17 @@ GetPNGDecoderAccountingLog()
 #define WIDTH_OFFSET 16
 #define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
 #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
 
 // First 8 bytes of a PNG file
 const uint8_t 
 nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
 
-nsPNGDecoder::nsPNGDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
+nsPNGDecoder::nsPNGDecoder(RasterImage &aImage, imgDecoderObserver* aObserver)
  : Decoder(aImage, aObserver),
    mPNG(nullptr), mInfo(nullptr),
    mCMSLine(nullptr), interlacebuf(nullptr),
    mInProfile(nullptr), mTransform(nullptr),
    mHeaderBuf(nullptr), mHeaderBytesRead(0),
    mChannels(0), mFrameIsHidden(false),
    mCMSMode(0), mDisablePremultipliedAlpha(false)
 {
@@ -100,24 +99,24 @@ void nsPNGDecoder::CreateFrame(png_uint_
   if (NS_FAILED(rv))
     longjmp(png_jmpbuf(mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
 
   mFrameRect.x = x_offset;
   mFrameRect.y = y_offset;
   mFrameRect.width = width;
   mFrameRect.height = height;
 
+  // Tell the superclass we're starting a frame
+  PostFrameStart();
+
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL))
     SetAnimFrameInfo();
 #endif
 
-  // Tell the superclass we're starting a frame
-  PostFrameStart();
-
   PR_LOG(GetPNGDecoderAccountingLog(), PR_LOG_DEBUG,
          ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created "
           "image frame with %dx%d pixels in container %p",
           width, height,
           &mImage));
 
   mFrameHasNoAlpha = true;
 }
@@ -144,17 +143,17 @@ void nsPNGDecoder::SetAnimFrameInfo()
       delay_den = 100; // so says the APNG spec
 
     // Need to cast delay_num to float to have a proper division and
     // the result to int to avoid compiler warning
     timeout = static_cast<int32_t>
               (static_cast<double>(delay_num) * 1000 / delay_den);
   }
 
-  uint32_t numFrames = mImage.GetNumFrames();
+  uint32_t numFrames = GetFrameCount();
 
   mImage.SetFrameTimeout(numFrames - 1, timeout);
 
   if (dispose_op == PNG_DISPOSE_OP_PREVIOUS)
       mImage.SetFrameDisposalMethod(numFrames - 1,
                                     RasterImage::kDisposeRestorePrevious);
   else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND)
       mImage.SetFrameDisposalMethod(numFrames - 1,
@@ -173,17 +172,17 @@ void nsPNGDecoder::SetAnimFrameInfo()
 // set timeout and frame disposal method for the current frame
 void nsPNGDecoder::EndImageFrame()
 {
   if (mFrameIsHidden)
     return;
 
   uint32_t numFrames = 1;
 #ifdef PNG_APNG_SUPPORTED
-  numFrames = mImage.GetNumFrames();
+  numFrames = GetFrameCount();
 
   // We can't use mPNG->num_frames_read as it may be one ahead.
   if (numFrames > 1) {
     // Tell the image renderer that the frame is complete
     if (mFrameHasNoAlpha)
       mImage.SetFrameHasNoAlpha(numFrames - 1);
 
     // PNG is always non-premult
@@ -785,17 +784,17 @@ nsPNGDecoder::row_callback(png_structp p
       break;
       default:
         longjmp(png_jmpbuf(decoder->mPNG), 1);
     }
 
     if (!rowHasNoAlpha)
       decoder->mFrameHasNoAlpha = false;
 
-    uint32_t numFrames = decoder->mImage.GetNumFrames();
+    uint32_t numFrames = decoder->GetFrameCount();
     if (numFrames <= 1) {
       // Only do incremental image display for the first frame
       // XXXbholley - this check should be handled in the superclass
       nsIntRect r(0, row_num, width, 1);
       decoder->PostInvalidation(r);
     }
   }
 }
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -4,33 +4,33 @@
  * 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 nsPNGDecoder_h__
 #define nsPNGDecoder_h__
 
 #include "Decoder.h"
 
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 #include "gfxASurface.h"
 
 #include "nsCOMPtr.h"
 
 #include "png.h"
 
 #include "qcms.h"
 
 namespace mozilla {
 namespace image {
 class RasterImage;
 
 class nsPNGDecoder : public Decoder
 {
 public:
-  nsPNGDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver);
+  nsPNGDecoder(RasterImage &aImage, imgDecoderObserver* aObserver);
   virtual ~nsPNGDecoder();
 
   virtual void InitInternal();
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
   virtual Telemetry::ID SpeedHistogram();
 
   void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                    int32_t width, int32_t height,
--- a/image/public/Makefile.in
+++ b/image/public/Makefile.in
@@ -14,18 +14,16 @@ MODULE		= imglib2
 GRE_MODULE	= 1
 
 EXPORTS		= ImageLogging.h
 
 XPIDLSRCS	= \
 		imgICache.idl             \
 		imgIContainer.idl         \
 		imgIContainerDebug.idl    \
-		imgIContainerObserver.idl \
-		imgIDecoderObserver.idl   \
 		imgIEncoder.idl           \
 		imgILoader.idl            \
 		imgINotificationObserver.idl \
 		imgIOnloadBlocker.idl     \
 		imgIRequest.idl           \
 		imgIScriptedNotificationObserver.idl \
 		imgITools.idl             \
 		$(NULL)
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -1,18 +1,16 @@
 /** -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface imgIDecoderObserver;
-
 %{C++
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "gfxPattern.h"
 #include "gfxASurface.h"
 #include "nsRect.h"
deleted file mode 100644
--- a/image/public/imgIContainerObserver.idl
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-%{C++
-#include "nsRect.h"
-%}
-
-interface imgIRequest;
-[ptr] native nsIntRect(nsIntRect);
-
-/**
- * imgIContainerObserver interface
- *
- * @author Stuart Parmenter <pavlov@netscape.com>
- * @version 0.1
- */
-[scriptable, uuid(f4aaf410-e88f-4036-b91c-610c9c11d825)]
-interface imgIContainerObserver : nsISupports
-{
-  [noscript] void frameChanged([const] in nsIntRect aDirtyRect);
-};
deleted file mode 100644
--- a/image/public/imgIDecoderObserver.idl
+++ /dev/null
@@ -1,120 +0,0 @@
-/** -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "imgIContainerObserver.idl"
-
-interface imgIRequest;
-interface imgIContainer;
-
-%{C++
-#include "nsRect.h"
-%}
-
-/**
- * imgIDecoderObserver interface
- *
- * This interface is used both for observing imgIDecoder objects and for
- * observing imgIRequest objects.  In the former case, aRequest is
- * always null.
- *
- * We make the distinction here between "load" and "decode" notifications. Load
- * notifications are fired as the image is loaded from the network or
- * filesystem. Decode notifications are fired as the image is decoded. If an
- * image is decoded on load and not visibly discarded, decode notifications are
- * nested logically inside load notifications as one might expect. However, with
- * decode-on-draw, the set of decode notifications can imgRcome completely _after_
- * the load notifications, and can come multiple times if the image is
- * discardable. Moreover, they can be interleaved in various ways. In general,
- * any presumed ordering between load and decode notifications should not be
- * relied upon.
- *
- * Decode notifications may or may not be synchronous, depending on the
- * situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
- * triggers a decode, all notifications that can be generated from the currently
- * loaded data fire before the call returns. If FLAG_SYNC_DECODE is not passed,
- * all, some, or none of the notifications may fire before the call returns.
- *
- * This interface will be cleaned up in bug 505385.
- *
- * @author Stuart Parmenter <pavlov@netscape.com>
- * @version 0.1
- * @see imagelib2
- */
-[scriptable, uuid(0089cf0c-210c-4b44-9ab0-8099b32bcf64)]
-interface imgIDecoderObserver : imgIContainerObserver
-{
-  /**
-   * Load notification.
-   *
-   * called at the same time that nsIRequestObserver::onStartRequest would be
-   * (used only for observers of imgIRequest objects, which are nsIRequests,
-   * not imgIDecoder objects)
-   */
-  void onStartRequest();
-
-  /**
-   * Decode notification.
-   *
-   * Called as soon as the image begins getting decoded. This does not include
-   * "header-only" decodes used by decode-on-draw to parse the width/height
-   * out of the image. Thus, it is a decode notification only.
-   */
-  void onStartDecode();
-
-  /**
-   * Load notification.
-   *
-   * Called once enough data has been loaded from the network that we were able
-   * to parse the width/height from the image. By the time this callback is been
-   * called, the size has been set on the container and STATUS_SIZE_AVAILABLE
-   * has been set on the associated imgRequest.
-   */
-  void onStartContainer();
-
-  /**
-   * Decode notification.
-   *
-   * called when there is more to paint.
-   */
-  [noscript] void onDataAvailable([const] in nsIntRect aRect);
-
-  /**
-   * Decode notification.
-   *
-   * called when a frame is finished decoding.
-   */
-  void onStopFrame();
-
-  /**
-   * Notification for when an image is known to be animated. This should be
-   * fired at the earliest possible time.
-   */
-  void onImageIsAnimated();
-
-  /**
-   * Decode notification.
-   *
-   * Called when all decoding has terminated.
-   */
-  void onStopDecode(in nsresult status);
-
-  /**
-   * Load notification.
-   *
-   * called at the same time that nsIRequestObserver::onStopRequest would be
-   * (used only for observers of imgIRequest objects, which are nsIRequests,
-   * not imgIDecoder objects)
-   */
-  void onStopRequest(in boolean aIsLastPart);
-
-  /**
-   * Called when the decoded image data is discarded. This means that the frames
-   * no longer exist in decoded form, and any attempt to access or draw the
-   * image will initiate a new series of progressive decode notifications.
-   */
-  void onDiscard();
-
-};
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -7,17 +7,17 @@
 #include "Decoder.h"
 #include "nsIServiceManager.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 
 namespace mozilla {
 namespace image {
 
-Decoder::Decoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
+Decoder::Decoder(RasterImage &aImage, imgDecoderObserver* aObserver)
   : mImage(aImage)
   , mObserver(aObserver)
   , mDecodeFlags(0)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mInitialized(false)
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -2,27 +2,27 @@
 /* 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_IMAGELIB_DECODER_H_
 #define MOZILLA_IMAGELIB_DECODER_H_
 
 #include "RasterImage.h"
-
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
+#include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace image {
 
 class Decoder
 {
 public:
 
-  Decoder(RasterImage& aImage, imgIDecoderObserver* aObserver);
+  Decoder(RasterImage& aImage, imgDecoderObserver* aObserver);
   virtual ~Decoder();
 
   /**
    * Initialize an image decoder. Decoders may not be re-initialized.
    *
    * @param aContainer The image container to decode to.
    * @param aObserver The observer for decode notification events.
    *
@@ -170,17 +170,17 @@ protected:
   void PostDataError();
   void PostDecoderError(nsresult aFailCode);
 
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
-  nsCOMPtr<imgIDecoderObserver> mObserver;
+  RefPtr<imgDecoderObserver> mObserver;
 
   uint32_t mDecodeFlags;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -42,17 +42,17 @@ public:
 
   /**
    * Creates a new image container.
    *
    * @param aObserver Observer to send decoder and animation notifications to.
    * @param aMimeType The mimetype of the image.
    * @param aFlags Initialization flags of the INIT_FLAG_* variety.
    */
-  virtual nsresult Init(imgIDecoderObserver* aObserver,
+  virtual nsresult Init(imgDecoderObserver* aObserver,
                         const char* aMimeType,
                         const char* aURIString,
                         uint32_t aFlags) = 0;
 
   /**
    * The rectangle defining the location and size of the currently displayed
    * frame.
    */
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1,20 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/histogram.h"
 #include "ImageLogging.h"
 #include "nsComponentManagerUtils.h"
-#include "imgIContainerObserver.h"
+#include "imgDecoderObserver.h"
 #include "nsError.h"
 #include "Decoder.h"
-#include "imgIDecoderObserver.h"
 #include "RasterImage.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsAutoPtr.h"
 #include "nsStringStream.h"
 #include "prenv.h"
 #include "ImageContainer.h"
 #include "Layers.h"
@@ -353,17 +352,16 @@ NS_IMPL_ISUPPORTS3(RasterImage, imgICont
 
 //******************************************************************************
 RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
   Image(aStatusTracker), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mAnim(nullptr),
   mLoopCount(-1),
-  mObserver(nullptr),
   mLockCount(0),
   mDecoder(nullptr),
   mDecodeRequest(this),
   mBytesDecoded(0),
   mDecodeCount(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
@@ -434,17 +432,17 @@ RasterImage::Initialize()
   InitPrefCaches();
 
   // Create our singletons now, so we don't have to worry about what thread
   // they're created on.
   DecodeWorker::Singleton();
 }
 
 nsresult
-RasterImage::Init(imgIDecoderObserver *aObserver,
+RasterImage::Init(imgDecoderObserver *aObserver,
                   const char* aMimeType,
                   const char* aURIString,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
@@ -457,17 +455,19 @@ RasterImage::Init(imgIDecoderObserver *a
   // We must be non-discardable and non-decode-on-draw for
   // multipart channels
   NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
                     (!(aFlags & INIT_FLAG_DISCARDABLE) &&
                      !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
                     "Can't be discardable or decode-on-draw for multipart");
 
   // Store initialization data
-  mObserver = do_GetWeakReference(aObserver);
+  if (aObserver) {
+    mObserver = aObserver->asWeakPtr();
+  }
   mSourceDataMimeType.Assign(aMimeType);
   mURIString.Assign(aURIString);
   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
   mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
   mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
 
   // Statistics
   if (mDiscardable) {
@@ -633,33 +633,31 @@ RasterImage::RequestRefresh(const mozill
     // then we need to break out of this loop & wait for the frame(s)
     // to finish downloading
     if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
       break;
     }
   }
 
   if (frameAdvanced) {
-    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
-
-    if (!observer) {
+    if (!mObserver) {
       NS_ERROR("Refreshing image after its imgRequest is gone");
       StopAnimation();
       return;
     }
 
     // Notify listeners that our frame has actually changed, but do this only
     // once for all frames that we've now passed (if AdvanceFrame() was called
     // more than once).
     #ifdef DEBUG
       mFramesNotified++;
     #endif
 
     UpdateImageContainer();
-    observer->FrameChanged(&dirtyRect);
+    mObserver->FrameChanged(&dirtyRect);
   }
 }
 
 //******************************************************************************
 /* [noscript] imgIContainer extractFrame(uint32_t aWhichFrame,
  *                                       [const] in nsIntRect aRegion,
  *                                       in uint32_t aFlags); */
 NS_IMETHODIMP
@@ -1655,19 +1653,18 @@ RasterImage::ResetAnimation()
   mAnim->lastCompositedFrameIndex = -1;
   mAnim->currentAnimationFrameIndex = 0;
   UpdateImageContainer();
 
   // Note - We probably want to kick off a redecode somewhere around here when
   // we fix bug 500402.
 
   // Update display if we were animating before
-  nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
-  if (mAnimating && observer)
-    observer->FrameChanged(&(mAnim->firstFrameRefreshArea));
+  if (mAnimating && mObserver)
+    mObserver->FrameChanged(&(mAnim->firstFrameRefreshArea));
 
   if (ShouldAnimate()) {
     StartAnimation();
     // The animation may not have been running before, if mAnimationFinished
     // was false (before we changed it to true in this function). So, mark the
     // animation as running.
     mAnimating = true;
   }
@@ -2463,19 +2460,18 @@ RasterImage::Discard(bool force)
   // Clear our downscaled frame.
   mScaleResult.status = SCALE_INVALID;
   mScaleResult.frame = nullptr;
 
   // Flag that we no longer have decoded frames for this image
   mDecoded = false;
 
   // Notify that we discarded
-  nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
-  if (observer)
-    observer->OnDiscard();
+  if (mObserver)
+    mObserver->OnDiscard();
 
   if (force)
     DiscardTracker::Remove(&mDiscardTrackerNode);
 
   // Log
   PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
          ("CompressedImageAccounting: discarded uncompressed image "
           "data from RasterImage %p (%s) - %d frames (cached count: %d); "
@@ -2535,40 +2531,39 @@ RasterImage::InitDecoder(bool aDoSizeDec
 
   // Since we're not decoded, we should not have a discard timer active
   NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
 
   // Figure out which decoder we want
   eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
   CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
 
-  nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
   // Instantiate the appropriate decoder
   switch (type) {
     case eDecoderType_png:
-      mDecoder = new nsPNGDecoder(*this, observer);
+      mDecoder = new nsPNGDecoder(*this, mObserver);
       break;
     case eDecoderType_gif:
-      mDecoder = new nsGIFDecoder2(*this, observer);
+      mDecoder = new nsGIFDecoder2(*this, mObserver);
       break;
     case eDecoderType_jpeg:
       // If we have all the data we don't want to waste cpu time doing
       // a progressive decode
-      mDecoder = new nsJPEGDecoder(*this, observer,
+      mDecoder = new nsJPEGDecoder(*this, mObserver,
                                    mHasBeenDecoded ? Decoder::SEQUENTIAL :
                                                      Decoder::PROGRESSIVE);
       break;
     case eDecoderType_bmp:
-      mDecoder = new nsBMPDecoder(*this, observer);
+      mDecoder = new nsBMPDecoder(*this, mObserver);
       break;
     case eDecoderType_ico:
-      mDecoder = new nsICODecoder(*this, observer);
+      mDecoder = new nsICODecoder(*this, mObserver);
       break;
     case eDecoderType_icon:
-      mDecoder = new nsIconDecoder(*this, observer);
+      mDecoder = new nsIconDecoder(*this, mObserver);
       break;
     default:
       NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
   }
 
   // Initialize the decoder
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
@@ -2907,17 +2902,17 @@ void
 RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
 {
   MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
   MOZ_ASSERT(request);
 
   if (status == SCALE_DONE) {
     MOZ_ASSERT(request->done);
 
-    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
+    RefPtr<imgDecoderObserver> observer(mObserver);
     if (observer) {
       imgFrame *scaledFrame = request->dstFrame.get();
       scaledFrame->ImageUpdated(scaledFrame->GetRect());
       observer->FrameChanged(&request->srcRect);
     }
 
     mScaleResult.status = SCALE_DONE;
     mScaleResult.frame = request->dstFrame;
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -19,33 +19,30 @@
 
 #include "Image.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsITimer.h"
 #include "nsIRequest.h"
-#include "nsWeakReference.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
 #include "nsThreadUtils.h"
 #include "DiscardTracker.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "gfx2DGlue.h"
 #ifdef DEBUG
   #include "imgIContainerDebug.h"
 #endif
 
-class imgIDecoder;
-class imgIContainerObserver;
 class nsIInputStream;
 
 #define NS_RASTERIMAGE_CID \
 { /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */         \
      0x376ff2c1,                                     \
      0x9bf6,                                         \
      0x418a,                                         \
     {0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \
@@ -155,17 +152,17 @@ public:
 
   // (no public constructor - use ImageFactory)
   virtual ~RasterImage();
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
 
   // Methods inherited from Image
-  nsresult Init(imgIDecoderObserver* aObserver,
+  nsresult Init(imgDecoderObserver* aObserver,
                 const char* aMimeType,
                 const char* aURIString,
                 uint32_t aFlags);
   virtual void  GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE;
 
   // Raster-specific methods
   static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
                                       const char* aFromRawSegment,
@@ -637,18 +634,17 @@ private: // data
   // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
   // that the frames actually exist (they may have been discarded to save memory, or
   // we maybe decoding on draw).
   RasterImage::Anim*        mAnim;
   
   //! # loops remaining before animation stops (-1 no stop)
   int32_t                    mLoopCount;
   
-  //! imgIDecoderObserver
-  nsWeakPtr                  mObserver;
+  mozilla::WeakPtr<imgDecoderObserver> mObserver;
 
   // Discard members
   uint32_t                   mLockCount;
   DiscardTracker::Node       mDiscardTrackerNode;
 
   // Source data members
   FallibleTArray<char>       mSourceData;
   nsCString                  mSourceDataMimeType;
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VectorImage.h"
 
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 #include "SVGDocumentWrapper.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
@@ -183,30 +183,32 @@ VectorImage::VectorImage(imgStatusTracke
 VectorImage::~VectorImage()
 {
 }
 
 //------------------------------------------------------------------------------
 // Methods inherited from Image.h
 
 nsresult
-VectorImage::Init(imgIDecoderObserver* aObserver,
+VectorImage::Init(imgDecoderObserver* aObserver,
                   const char* aMimeType,
                   const char* aURIString,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mIsInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
   NS_ABORT_IF_FALSE(!mIsFullyLoaded && !mHaveAnimations &&
                     !mHaveRestrictedRegion && !mError,
                     "Flags unexpectedly set before initialization");
 
-  mObserver = do_GetWeakReference(aObserver);
+  if (aObserver) {
+    mObserver = aObserver->asWeakPtr();
+  }
   NS_ABORT_IF_FALSE(!strcmp(aMimeType, SVG_MIMETYPE), "Unexpected mimetype");
 
   mIsInitialized = true;
 
   return NS_OK;
 }
 
 void
@@ -704,17 +706,17 @@ VectorImage::OnStopRequest(nsIRequest* a
 
   mIsFullyLoaded = true;
   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
 
   // Start listening to our image for rendering updates
   mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
 
   // Tell *our* observers that we're done loading
-  nsCOMPtr<imgIDecoderObserver> observer = do_QueryReferent(mObserver);
+  RefPtr<imgDecoderObserver> observer(mObserver);
   if (observer) {
     // NOTE: This signals that width/height are available.
     observer->OnStartContainer();
 
     observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
     observer->OnStopFrame();
     observer->OnStopDecode(NS_OK);
   }
@@ -743,24 +745,17 @@ VectorImage::OnDataAvailable(nsIRequest*
 }
 
 // --------------------------
 // Invalidation helper method
 
 void
 VectorImage::InvalidateObserver()
 {
-  if (!mObserver)
-    return;
-
-  nsCOMPtr<imgIContainerObserver> containerObs(do_QueryReferent(mObserver));
-  if (containerObs) {
-    containerObs->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
-  }
-
-  nsCOMPtr<imgIDecoderObserver> decoderObs(do_QueryReferent(mObserver));
-  if (decoderObs) {
-    decoderObs->OnStopFrame();
+  RefPtr<imgDecoderObserver> observer(mObserver);
+  if (observer) {
+    observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
+    observer->OnStopFrame();
   }
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -4,20 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_imagelib_VectorImage_h_
 #define mozilla_imagelib_VectorImage_h_
 
 #include "Image.h"
 #include "nsIStreamListener.h"
 #include "nsIRequest.h"
-#include "nsWeakReference.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/WeakPtr.h"
 
-class imgIDecoderObserver;
+class imgDecoderObserver;
 
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
 }
 namespace image {
 
@@ -32,17 +32,17 @@ public:
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_IMGICONTAINER
 
   // (no public constructor - use ImageFactory)
   virtual ~VectorImage();
 
   // Methods inherited from Image
-  nsresult Init(imgIDecoderObserver* aObserver,
+  nsresult Init(imgDecoderObserver* aObserver,
                 const char* aMimeType,
                 const char* aURIString,
                 uint32_t aFlags);
   virtual void GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE;
 
   virtual size_t HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const;
   virtual size_t NonHeapSizeOfDecoded() const;
@@ -64,17 +64,17 @@ public:
 protected:
   VectorImage(imgStatusTracker* aStatusTracker = nullptr);
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
   virtual bool     ShouldAnimate();
 
 private:
-  nsWeakPtr                          mObserver;   //! imgIDecoderObserver
+  WeakPtr<imgDecoderObserver>        mObserver;
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
   nsRefPtr<SVGRootRenderingObserver> mRenderingObserver;
 
   nsIntRect      mRestrictedRegion;       // If we were created by
                                           // ExtractFrame, this is the region
                                           // that we're restricted to using.
                                           // Otherwise, this is ignored.
 
new file mode 100644
--- /dev/null
+++ b/image/src/imgDecoderObserver.h
@@ -0,0 +1,121 @@
+/** -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_IMAGELIB_IMGDECODEROBSERVER_H_
+#define MOZILLA_IMAGELIB_IMGDECODEROBSERVER_H_
+
+#include "nsRect.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+
+/**
+ * imgDecoderObserver interface
+ *
+ * This interface is used to observe Decoder objects.
+ *
+ * We make the distinction here between "load" and "decode" notifications. Load
+ * notifications are fired as the image is loaded from the network or
+ * filesystem. Decode notifications are fired as the image is decoded. If an
+ * image is decoded on load and not visibly discarded, decode notifications are
+ * nested logically inside load notifications as one might expect. However, with
+ * decode-on-draw, the set of decode notifications can come completely _after_
+ * the load notifications, and can come multiple times if the image is
+ * discardable. Moreover, they can be interleaved in various ways. In general,
+ * any presumed ordering between load and decode notifications should not be
+ * relied upon.
+ *
+ * Decode notifications may or may not be synchronous, depending on the
+ * situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
+ * triggers a decode, all notifications that can be generated from the currently
+ * loaded data fire before the call returns. If FLAG_SYNC_DECODE is not passed,
+ * all, some, or none of the notifications may fire before the call returns.
+ */
+class imgDecoderObserver : public mozilla::RefCounted<imgDecoderObserver>,
+                           public mozilla::SupportsWeakPtr<imgDecoderObserver>
+{
+public:
+  virtual ~imgDecoderObserver() = 0;
+
+  /**
+   * Load notification.
+   *
+   * called at the same time that nsIRequestObserver::onStartRequest would be
+   * (used only for observers of imgIRequest objects, which are nsIRequests,
+   * not imgIDecoder objects)
+   */
+  virtual void OnStartRequest() = 0;
+
+  /**
+   * Decode notification.
+   *
+   * Called as soon as the image begins getting decoded. This does not include
+   * "header-only" decodes used by decode-on-draw to parse the width/height
+   * out of the image. Thus, it is a decode notification only.
+   */
+  virtual void OnStartDecode() = 0;
+
+  /**
+   * Load notification.
+   *
+   * Called once enough data has been loaded from the network that we were able
+   * to parse the width/height from the image. By the time this callback is been
+   * called, the size has been set on the container and STATUS_SIZE_AVAILABLE
+   * has been set on the associated imgRequest.
+   */
+  virtual void OnStartContainer() = 0;
+
+  /**
+   * Decode notification.
+   *
+   * called when there is more to paint.
+   */
+  virtual void OnDataAvailable(const nsIntRect * aRect) = 0;
+
+  virtual void FrameChanged(const nsIntRect * aDirtyRect) = 0;
+
+  /**
+   * Decode notification.
+   *
+   * called when a frame is finished decoding.
+   */
+  virtual void OnStopFrame() = 0;
+
+  /**
+   * Notification for when an image is known to be animated. This should be
+   * fired at the earliest possible time.
+   */
+  virtual void OnImageIsAnimated() = 0;
+
+  /**
+   * Decode notification.
+   *
+   * Called when all decoding has terminated.
+   */
+  virtual void OnStopDecode(nsresult status) = 0;
+
+  /**
+   * Load notification.
+   *
+   * called at the same time that nsIRequestObserver::onStopRequest would be
+   * (used only for observers of imgIRequest objects, which are nsIRequests,
+   * not imgIDecoder objects)
+   */
+  virtual void OnStopRequest(bool aIsLastPart) = 0;
+
+  /**
+   * Called when the decoded image data is discarded. This means that the frames
+   * no longer exist in decoded form, and any attempt to access or draw the
+   * image will initiate a new series of progressive decode notifications.
+   */
+  virtual void OnDiscard() = 0;
+};
+
+// We must define a destructor because derived classes call our destructor from
+// theirs.  Pure virtual destructors only requires that child classes implement
+// a virtual destructor, not that we can't have one too!
+inline imgDecoderObserver::~imgDecoderObserver()
+{}
+
+#endif // MOZILLA_IMAGELIB_IMGDECODEROBSERVER_H
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -672,17 +672,17 @@ NS_IMETHODIMP imgRequestProxy::GetHasTra
     *hasData = GetOwner()->HasTransferredData();
   } else {
     // The safe thing to do is to claim we have data
     *hasData = true;
   }
   return NS_OK;
 }
 
-/** imgIDecoderObserver methods **/
+/** imgDecoderObserver methods **/
 
 void imgRequestProxy::OnStartContainer()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
 
   if (mListener && !mCanceled && !mSentStartContainer) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -130,17 +130,17 @@ protected:
       nsRefPtr<imgRequestProxy> mOwner;
       nsresult mStatus;
   };
 
   // The following notification functions are protected to ensure that (friend
   // class) imgStatusTracker is the only class allowed to send us
   // notifications.
 
-  /* non-virtual imgIDecoderObserver methods */
+  /* non-virtual imgDecoderObserver methods */
   void OnStartContainer  ();
   void OnFrameUpdate     (const nsIntRect * aRect);
   void OnStopFrame       ();
   void OnStopDecode      ();
   void OnDiscard         ();
   void OnImageIsAnimated ();
 
   /* non-virtual sort-of-nsIRequestObserver methods */
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -4,229 +4,216 @@
  * 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 "imgStatusTracker.h"
 
 #include "imgRequest.h"
 #include "imgIContainer.h"
 #include "imgRequestProxy.h"
+#include "imgDecoderObserver.h"
 #include "Image.h"
 #include "ImageLogging.h"
 #include "RasterImage.h"
 #include "nsIObserverService.h"
 
 #include "mozilla/Util.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Services.h"
 
 using namespace mozilla::image;
 
-NS_IMPL_ISUPPORTS3(imgStatusTrackerObserver,
-                   imgIDecoderObserver,
-                   imgIContainerObserver,
-                   nsISupportsWeakReference)
-
-/** imgIContainerObserver methods **/
-
-/* [noscript] void frameChanged (in nsIntRect dirtyRect); */
-NS_IMETHODIMP imgStatusTrackerObserver::FrameChanged(const nsIntRect *dirtyRect)
-{
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "FrameChanged callback before we've created our image");
-
-  mTracker->RecordFrameChanged(dirtyRect);
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendFrameChanged(iter.GetNext(), dirtyRect);
-  }
-
-  return NS_OK;
-}
-
-/** imgIDecoderObserver methods **/
-
-NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode()
-{
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnStartDecode callback before we've created our image");
-
-  if (mTracker->GetRequest() && !mTracker->GetRequest()->GetMultipart()) {
-    MOZ_ASSERT(!mTracker->mBlockingOnload);
-    mTracker->mBlockingOnload = true;
-
-    mTracker->RecordBlockOnload();
-
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-    while (iter.HasMore()) {
-      mTracker->SendBlockOnload(iter.GetNext());
-    }
-  }
-
-  /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
-     indicates the beginning of a new decode.
-     The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
-     the code in imgStatusTrackerObserver::OnStopFrame will continue to increase the data size cumulatively.
-  */
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->ResetCacheEntry();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP imgStatusTrackerObserver::OnStartRequest()
-{
-  NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
-  return NS_OK;
-}
-
-/* void onStartContainer (); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnStartContainer()
-{
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer");
-
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnStartContainer callback before we've created our image");
-  mTracker->RecordStartContainer(mTracker->GetImage());
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStartContainer(iter.GetNext());
-  }
-
-  return NS_OK;
-}
-
-/* [noscript] void onDataAvailable ([const] in nsIntRect rect); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnDataAvailable(const nsIntRect * rect)
-{
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDataAvailable");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnDataAvailable callback before we've created our image");
-
-  mTracker->RecordDataAvailable();
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendDataAvailable(iter.GetNext(), rect);
-  }
-
-  return NS_OK;
-}
-
-/* void onStopFrame (); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnStopFrame()
-{
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnStopFrame callback before we've created our image");
-
-  mTracker->RecordStopFrame();
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStopFrame(iter.GetNext());
-  }
-
-  mTracker->MaybeUnblockOnload();
-
-  return NS_OK;
-}
-
 static void
 FireFailureNotification(imgRequest* aRequest)
 {
   // Some kind of problem has happened with image decoding.
   // Report the URI to net:failed-to-process-uri-conent observers.
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     nsCOMPtr<nsIURI> uri;
     aRequest->GetURI(getter_AddRefs(uri));
     os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
   }
 }
 
-/* void onStopDecode (in nsresult status); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(nsresult aStatus)
+class imgStatusTrackerObserver : public imgDecoderObserver
 {
-  LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnStopDecode callback before we've created our image");
+public:
+  imgStatusTrackerObserver(imgStatusTracker* aTracker)
+  : mTracker(aTracker) {}
 
-  // We finished the decode, and thus have the decoded frames. Update the cache
-  // entry size to take this into account.
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->UpdateCacheEntrySize();
+  virtual ~imgStatusTrackerObserver() {}
+
+  void SetTracker(imgStatusTracker* aTracker) {
+    mTracker = aTracker;
   }
 
-  bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR;
+  /** imgDecoderObserver methods **/
 
-  mTracker->RecordStopDecode(aStatus);
+  virtual void OnStartDecode()
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode");
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnStartDecode callback before we've created our image");
+
+    if (mTracker->GetRequest() && !mTracker->GetRequest()->GetMultipart()) {
+      MOZ_ASSERT(!mTracker->mBlockingOnload);
+      mTracker->mBlockingOnload = true;
+
+      mTracker->RecordBlockOnload();
 
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStopDecode(iter.GetNext(), aStatus);
+      nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+      while (iter.HasMore()) {
+        mTracker->SendBlockOnload(iter.GetNext());
+      }
+    }
+
+    /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
+       indicates the beginning of a new decode.
+       The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
+       the code in imgStatusTrackerObserver::OnStopFrame will continue to increase the data size cumulatively.
+    */
+    if (mTracker->GetRequest()) {
+      mTracker->GetRequest()->ResetCacheEntry();
+    }
   }
 
-  // This is really hacky. We need to handle the case where we start decoding,
-  // block onload, but then hit an error before we get to our first frame.
-  mTracker->MaybeUnblockOnload();
+  virtual void OnStartRequest()
+  {
+    NS_NOTREACHED("imgRequest(imgDecoderObserver)::OnStartRequest");
+  }
+
+  virtual void OnStartContainer()
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer");
+
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnStartContainer callback before we've created our image");
+    mTracker->RecordStartContainer(mTracker->GetImage());
 
-  if (NS_FAILED(aStatus) && !preexistingError && mTracker->GetRequest()) {
-    FireFailureNotification(mTracker->GetRequest());
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendStartContainer(iter.GetNext());
+    }
+  }
+
+  virtual void OnDataAvailable(const nsIntRect* rect)
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDataAvailable");
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnDataAvailable callback before we've created our image");
+
+    mTracker->RecordDataAvailable();
+
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendDataAvailable(iter.GetNext(), rect);
+    }
   }
 
-  return NS_OK;
-}
+  virtual void FrameChanged(const nsIntRect* dirtyRect)
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged");
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "FrameChanged callback before we've created our image");
 
-NS_IMETHODIMP imgStatusTrackerObserver::OnStopRequest(bool aLastPart)
-{
-  NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
-  return NS_OK;
-}
+    mTracker->RecordFrameChanged(dirtyRect);
+
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendFrameChanged(iter.GetNext(), dirtyRect);
+    }
+  }
 
-/* void onDiscard (); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnDiscard()
-{
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnDiscard callback before we've created our image");
+  virtual void OnStopFrame()
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame");
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnStopFrame callback before we've created our image");
 
-  mTracker->RecordDiscard();
+    mTracker->RecordStopFrame();
 
-  // Update the cache entry size, since we just got rid of frame data
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->UpdateCacheEntrySize();
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendStopFrame(iter.GetNext());
+    }
+
+    mTracker->MaybeUnblockOnload();
   }
 
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendDiscard(iter.GetNext());
+  virtual void OnStopDecode(nsresult aStatus)
+  {
+    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode");
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnStopDecode callback before we've created our image");
+
+    // We finished the decode, and thus have the decoded frames. Update the cache
+    // entry size to take this into account.
+    if (mTracker->GetRequest()) {
+      mTracker->GetRequest()->UpdateCacheEntrySize();
+    }
+
+    bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR;
+
+    mTracker->RecordStopDecode(aStatus);
+
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendStopDecode(iter.GetNext(), aStatus);
+    }
+
+    // This is really hacky. We need to handle the case where we start decoding,
+    // block onload, but then hit an error before we get to our first frame.
+    mTracker->MaybeUnblockOnload();
+
+    if (NS_FAILED(aStatus) && !preexistingError && mTracker->GetRequest()) {
+      FireFailureNotification(mTracker->GetRequest());
+    }
+  }
+
+  virtual void OnStopRequest(bool aLastPart)
+  {
+    NS_NOTREACHED("imgRequest(imgDecoderObserver)::OnStopRequest");
   }
 
-  return NS_OK;
-}
+  virtual void OnDiscard()
+  {
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnDiscard callback before we've created our image");
+
+    mTracker->RecordDiscard();
 
-NS_IMETHODIMP imgStatusTrackerObserver::OnImageIsAnimated()
-{
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnImageIsAnimated callback before we've created our image");
-  mTracker->RecordImageIsAnimated();
+    // Update the cache entry size, since we just got rid of frame data
+    if (mTracker->GetRequest()) {
+      mTracker->GetRequest()->UpdateCacheEntrySize();
+    }
 
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendImageIsAnimated(iter.GetNext());
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendDiscard(iter.GetNext());
+    }
   }
 
-  return NS_OK;
-}
+  virtual void OnImageIsAnimated()
+  {
+    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+                      "OnImageIsAnimated callback before we've created our image");
+    mTracker->RecordImageIsAnimated();
+
+    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+    while (iter.HasMore()) {
+      mTracker->SendImageIsAnimated(iter.GetNext());
+    }
+  }
+
+private:
+  imgStatusTracker* mTracker;
+};
+
 
 // imgStatusTracker methods
 
 imgStatusTracker::imgStatusTracker(Image* aImage, imgRequest* aRequest)
   : mImage(aImage),
     mRequest(aRequest),
     mState(0),
     mImageStatus(imgIRequest::STATUS_NONE),
@@ -242,16 +229,19 @@ imgStatusTracker::imgStatusTracker(const
     mImageStatus(aOther.mImageStatus),
     mHadLastPart(aOther.mHadLastPart),
     mBlockingOnload(aOther.mBlockingOnload)
     // Note: we explicitly don't copy mRequestRunnable, because it won't be
     // nulled out when the mRequestRunnable's Run function eventually gets
     // called.
 {}
 
+imgStatusTracker::~imgStatusTracker()
+{}
+
 void
 imgStatusTracker::SetImage(Image* aImage)
 {
   NS_ABORT_IF_FALSE(aImage, "Setting null image");
   NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one");
   mImage = aImage;
 }
 
@@ -610,17 +600,16 @@ imgStatusTracker::RecordImageIsAnimated(
 
 void
 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
 {
   if (!aProxy->NotificationsDeferred())
     aProxy->OnDiscard();
 }
 
-/* non-virtual imgIContainerObserver methods */
 void
 imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect)
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordFrameChanged called before we have an Image");
   // no bookkeeping necessary here - this is only for in-frame updates, which we
   // don't fire while we're recording
 }
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -7,63 +7,41 @@
 #ifndef imgStatusTracker_h__
 #define imgStatusTracker_h__
 
 class imgIContainer;
 class imgRequest;
 class imgRequestProxy;
 class imgStatusNotifyRunnable;
 class imgRequestNotifyRunnable;
-class imgStatusTracker;
+class imgStatusTrackerObserver;
 struct nsIntRect;
 namespace mozilla {
 namespace image {
 class Image;
 } // namespace image
 } // namespace mozilla
 
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsTObserverArray.h"
 #include "nsIRunnable.h"
 #include "nscore.h"
-#include "nsWeakReference.h"
-#include "imgIDecoderObserver.h"
+#include "imgDecoderObserver.h"
 
 enum {
   stateRequestStarted    = 1u << 0,
   stateHasSize           = 1u << 1,
   stateDecodeStopped     = 1u << 3,
   stateFrameStopped      = 1u << 4,
   stateRequestStopped    = 1u << 5,
   stateBlockingOnload    = 1u << 6
 };
 
-class imgStatusTrackerObserver : public imgIDecoderObserver,
-                                 public nsSupportsWeakReference
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_IMGIDECODEROBSERVER
-  NS_DECL_IMGICONTAINEROBSERVER
-
-  imgStatusTrackerObserver(imgStatusTracker* aTracker)
-  : mTracker(aTracker) {}
-
-  virtual ~imgStatusTrackerObserver() {}
-
-  void SetTracker(imgStatusTracker* aTracker) {
-    mTracker = aTracker;
-  }
-
-private:
-  imgStatusTracker* mTracker;
-};
-
 /*
  * The image status tracker is a class that encapsulates all the loading and
  * decoding status about an Image, and makes it possible to send notifications
  * to imgRequestProxys, both synchronously (i.e., the status now) and
  * asynchronously (the status later).
  *
  * When a new proxy needs to be notified of the current state of an image, call
  * the Notify() method on this class with the relevant proxy as its argument,
@@ -73,16 +51,17 @@ private:
 class imgStatusTracker
 {
 public:
   // aImage is the image that this status tracker will pass to the
   // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
   // alive as long as this instance is, because we hold a weak reference to it.
   imgStatusTracker(mozilla::image::Image* aImage, imgRequest* aRequest);
   imgStatusTracker(const imgStatusTracker& aOther);
+  ~imgStatusTracker();
 
   // Image-setter, for imgStatusTrackers created by imgRequest::Init, which
   // are created before their Image is created.  This method should only
   // be called once, and only on an imgStatusTracker that was initialized
   // without an image.
   void SetImage(mozilla::image::Image* aImage);
 
   // Schedule an asynchronous "replaying" of all the notifications that would
@@ -142,34 +121,32 @@ public:
   // Shorthand for recording all the load notifications: StartRequest,
   // StartContainer, StopRequest.
   void RecordLoaded();
 
   // Shorthand for recording all the decode notifications: StartDecode,
   // StartFrame, DataAvailable, StopFrame, StopDecode.
   void RecordDecoded();
 
-  /* non-virtual imgIDecoderObserver methods */
+  /* non-virtual imgDecoderObserver methods */
   void RecordStartContainer(imgIContainer* aContainer);
   void SendStartContainer(imgRequestProxy* aProxy);
   void RecordDataAvailable();
   void SendDataAvailable(imgRequestProxy* aProxy, const nsIntRect* aRect);
+  void RecordFrameChanged(const nsIntRect* aDirtyRect);
+  void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect);
   void RecordStopFrame();
   void SendStopFrame(imgRequestProxy* aProxy);
   void RecordStopDecode(nsresult statusg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
   void RecordDiscard();
   void SendDiscard(imgRequestProxy* aProxy);
   void RecordImageIsAnimated();
   void SendImageIsAnimated(imgRequestProxy *aProxy);
 
-  /* non-virtual imgIContainerObserver methods */
-  void RecordFrameChanged(const nsIntRect* aDirtyRect);
-  void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect);
-
   /* non-virtual sort-of-nsIRequestObserver methods */
   void RecordStartRequest();
   void SendStartRequest(imgRequestProxy* aProxy);
   void RecordStopRequest(bool aLastPart, nsresult aStatus);
   void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus);
 
   void OnStartRequest();
   void OnDataAvailable();
@@ -188,17 +165,17 @@ public:
 
   // Null out any reference to an associated image request
   void ClearRequest();
 
   // Weak pointer getters - no AddRefs.
   inline mozilla::image::Image* GetImage() const { return mImage; }
   inline imgRequest* GetRequest() const { return mRequest; }
 
-  inline imgIDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
+  inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
 
 private:
   friend class imgStatusNotifyRunnable;
   friend class imgRequestNotifyRunnable;
   friend class imgStatusTrackerObserver;
 
   nsCOMPtr<nsIRunnable> mRequestRunnable;
 
@@ -210,12 +187,12 @@ private:
   uint32_t mImageStatus;
   bool mHadLastPart;
   bool mBlockingOnload;
 
   // List of proxies attached to the image. Each proxy represents a consumer
   // using the image.
   nsTObserverArray<imgRequestProxy*> mConsumers;
 
-  nsRefPtr<imgStatusTrackerObserver> mTrackerObserver;
+  mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;
 };
 
 #endif
--- a/image/src/imgTools.cpp
+++ b/image/src/imgTools.cpp
@@ -7,18 +7,16 @@
 #include "imgTools.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsError.h"
 #include "imgLoader.h"
 #include "imgICache.h"
 #include "imgIContainer.h"
 #include "imgIEncoder.h"
-#include "imgIDecoderObserver.h"
-#include "imgIContainerObserver.h"
 #include "gfxContext.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsWeakReference.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsStreamUtils.h"
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
--- a/image/test/browser/head.js
+++ b/image/test/browser/head.js
@@ -19,19 +19,8 @@ function actOnMozImage(doc, id, func) {
     mozImage = imgContainer.QueryInterface(Ci.imgIContainerDebug);
   }
   catch (e) {
     return false;
   }
   func(mozImage);
   return true;
 }
-
-function awaitImageContainer(doc, id, func) {
-  getImageLoading(doc, id).addObserver({
-    QueryInterface: XPCOMUtils.generateQI([Ci.imgIDecoderObserver]),
-    onStartContainer: function(aRequest, aContainer) {
-      getImageLoading(doc, id).removeObserver(this);
-      func(aContainer);
-    },
-  });
-}
-
--- a/image/test/unit/async_load_tests.js
+++ b/image/test/unit/async_load_tests.js
@@ -121,19 +121,19 @@ function getChannelLoadImageStartCallbac
   }
 }
 
 // Return a closure that allows us to check the stream listener's status when the
 // image finishes loading.
 function getChannelLoadImageStopCallback(streamlistener, next)
 {
   return function channelLoadStop(imglistener, aRequest) {
-    // We absolutely must not get imgIDecoderObserver::onStopRequest after
-    // nsIRequestObserver::onStopRequest has fired. If we do that, we've broken
-    // people's expectations by delaying events from a channel we were given.
+    // We absolutely must not get imgIScriptedNotificationObserver::onStopRequest
+    // after nsIRequestObserver::onStopRequest has fired. If we do that, we've
+    // broken people's expectations by delaying events from a channel we were given.
     do_check_eq(streamlistener.requestStatus & STOP_REQUEST, 0);
 
     next();
 
     do_test_finished();
   }
 }
 
--- a/image/test/unit/image_load_helpers.js
+++ b/image/test/unit/image_load_helpers.js
@@ -12,18 +12,18 @@ const DATA_AVAILABLE = 0x04;
 // ImageListener.state.
 const SIZE_AVAILABLE = 0x01;
 const FRAME_UPDATE = 0x02;
 const FRAME_COMPLETE = 0x04;
 const LOAD_COMPLETE = 0x08;
 const DECODE_COMPLETE = 0x10;
 const ALL_BITS = SIZE_AVAILABLE | FRAME_COMPLETE | DECODE_COMPLETE | LOAD_COMPLETE;
 
-// An implementation of imgIDecoderObserver with the ability to call specified
-// functions on onStartRequest and onStopRequest.
+// An implementation of imgIScriptedNotificationObserver with the ability to
+// call specified functions on onStartRequest and onStopRequest.
 function ImageListener(start_callback, stop_callback)
 {
   this.sizeAvailable = function onSizeAvailable(aRequest)
   {
     do_check_false(this.synchronous);
 
     this.state |= SIZE_AVAILABLE;
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -405,16 +405,50 @@ GeckoChildProcessHost::PerformAsyncLaunc
   bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
 
   // Revert to original value
   PR_SetEnv(restoreOrigLogName);
 
   return retval;
 }
 
+void
+#if defined(XP_WIN)
+AddAppDirToCommandLine(CommandLine& aCmdLine)
+#else
+AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
+#endif
+{
+  // Content processes need access to application resources, so pass
+  // the full application directory path to the child process.
+  if (ShouldHaveDirectoryService()) {
+    nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+    NS_ASSERTION(directoryService, "Expected XPCOM to be available");
+    if (directoryService) {
+      nsCOMPtr<nsIFile> appDir;
+      // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the
+      // current process dir.
+      nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+                                          NS_GET_IID(nsIFile),
+                                          getter_AddRefs(appDir));
+      if (NS_SUCCEEDED(rv)) {
+        nsAutoCString path;
+        appDir->GetNativePath(path);
+#if defined(XP_WIN)
+        aCmdLine.AppendLooseValue(UTF8ToWide("-appdir"));
+        aCmdLine.AppendLooseValue(UTF8ToWide(path.get()));
+#else
+        aCmdLine.push_back("-appdir");
+        aCmdLine.push_back(path.get());
+#endif
+      }
+    }
+  }
+}
+
 bool
 GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
 {
   // We rely on the fact that InitializeChannel() has already been processed
   // on the IO thread before this point is reached.
   if (!GetChannel()) {
     return false;
   }
@@ -570,16 +604,19 @@ GeckoChildProcessHost::PerformAsyncLaunc
     }
     file = Omnijar::GetPath(Omnijar::APP);
     if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
       childArgv.push_back("-appomni");
       childArgv.push_back(path.get());
     }
   }
 
+  // Add the application directory path (-appdir path)
+  AddAppDirToCommandLine(childArgv);
+
   childArgv.push_back(pidstring);
 
 #if defined(MOZ_CRASHREPORTER)
 #  if defined(OS_LINUX) || defined(OS_BSD)
   int childCrashFd, childCrashRemapFd;
   if (!CrashReporter::CreateNotificationPipeForChild(
         &childCrashFd, &childCrashRemapFd))
     return false;
@@ -669,41 +706,51 @@ GeckoChildProcessHost::PerformAsyncLaunc
   cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
 
   for (std::vector<std::string>::iterator it = aExtraOpts.begin();
        it != aExtraOpts.end();
        ++it) {
       cmdLine.AppendLooseValue(UTF8ToWide(*it));
   }
 
-  cmdLine.AppendLooseValue(std::wstring(mGroupId.get()));
-
   if (Omnijar::IsInitialized()) {
     // Make sure the child process can find the omnijar
     // See XRE_InitCommandLine in nsAppRunner.cpp
     nsAutoString path;
     nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
     if (file && NS_SUCCEEDED(file->GetPath(path))) {
       cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
       cmdLine.AppendLooseValue(path.get());
     }
     file = Omnijar::GetPath(Omnijar::APP);
     if (file && NS_SUCCEEDED(file->GetPath(path))) {
       cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
       cmdLine.AppendLooseValue(path.get());
     }
   }
 
+  // Add the application directory path (-appdir path)
+  AddAppDirToCommandLine(cmdLine);
+
+  // XXX Command line params past this point are expected to be at
+  // the end of the command line string, and in a specific order.
+  // See XRE_InitChildProcess in nsEmbedFunction.
+
+  // Win app model id
+  cmdLine.AppendLooseValue(std::wstring(mGroupId.get()));
+
+  // Process id
   cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
 
 #if defined(MOZ_CRASHREPORTER)
   cmdLine.AppendLooseValue(
     UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
 #endif
 
+  // Process type
   cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
 
   base::LaunchApp(cmdLine, false, false, &process);
 
 #else
 #  error Sorry
 #endif
 
--- a/ipc/glue/ScopedXREEmbed.cpp
+++ b/ipc/glue/ScopedXREEmbed.cpp
@@ -24,16 +24,29 @@ ScopedXREEmbed::ScopedXREEmbed()
 
 ScopedXREEmbed::~ScopedXREEmbed()
 {
   Stop();
   NS_LogTerm();
 }
 
 void
+ScopedXREEmbed::SetAppDir(const nsACString& aPath)
+{
+  bool flag;
+  nsresult rv =
+    XRE_GetFileFromPath(aPath.BeginReading(), getter_AddRefs(mAppDir));
+  if (NS_FAILED(rv) ||
+      NS_FAILED(mAppDir->Exists(&flag)) || !flag) {
+    NS_WARNING("Invalid application directory passed to content process.");
+    mAppDir = nullptr;
+  }
+}
+
+void
 ScopedXREEmbed::Start()
 {
   std::string path;
 #if defined(OS_WIN)
   path = WideToUTF8(CommandLine::ForCurrentProcess()->program());
 #elif defined(OS_POSIX)
   path = CommandLine::ForCurrentProcess()->argv()[0];
 #else
@@ -75,17 +88,20 @@ ScopedXREEmbed::Start()
     if (NS_FAILED(rv))
       return;
 
     localFile = do_QueryInterface(parent);
     NS_ENSURE_TRUE_VOID(localFile);
   }
 #endif
 
-  rv = XRE_InitEmbedding2(localFile, localFile, nullptr);
+  if (mAppDir)
+    rv = XRE_InitEmbedding2(localFile, mAppDir, nullptr);
+  else
+    rv = XRE_InitEmbedding2(localFile, localFile, nullptr);
   if (NS_FAILED(rv))
     return;
 
   mShouldKillEmbedding = true;
 }
 
 void
 ScopedXREEmbed::Stop()
--- a/ipc/glue/ScopedXREEmbed.h
+++ b/ipc/glue/ScopedXREEmbed.h
@@ -1,27 +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/. */
 
 #ifndef __IPC_GLUE_SCOPEDXREEMBED_H__
 #define __IPC_GLUE_SCOPEDXREEMBED_H__
 
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "nsIFile.h"
+
 namespace mozilla {
 namespace ipc {
 
 class ScopedXREEmbed
 {
 public:
   ScopedXREEmbed();
   ~ScopedXREEmbed();
 
   void Start();
   void Stop();
+  void SetAppDir(const nsACString& aPath);
 
 private:
   bool mShouldKillEmbedding;
+  nsCOMPtr<nsIFile> mAppDir;
 };
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_SCOPEDXREEMBED_H__ */
\ No newline at end of file
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1497,23 +1497,37 @@ js::CallTracer(JSTracer *trc, void *thin
 }
 
 static void
 UnmarkGrayGCThing(void *thing)
 {
     static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
 }
 
+static void
+UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind);
+
 struct UnmarkGrayTracer : public JSTracer
 {
-    UnmarkGrayTracer() : tracingShape(false), previousShape(NULL) {}
+    /*
+     * We set eagerlyTraceWeakMaps to false because the cycle collector will fix
+     * up any color mismatches involving weakmaps when it runs.
+     */
+    UnmarkGrayTracer(JSRuntime *rt)
+      : tracingShape(false), previousShape(NULL)
+    {
+        JS_TracerInit(this, rt, UnmarkGrayChildren);
+        eagerlyTraceWeakMaps = false;
+    }
+
     UnmarkGrayTracer(JSTracer *trc, bool tracingShape)
-        : tracingShape(tracingShape), previousShape(NULL)
+      : tracingShape(tracingShape), previousShape(NULL)
     {
-        JS_TracerInit(this, trc->runtime, trc->callback);
+        JS_TracerInit(this, trc->runtime, UnmarkGrayChildren);
+        eagerlyTraceWeakMaps = false;
     }
 
     /* True iff we are tracing the immediate children of a shape. */
     bool tracingShape;
 
     /* If tracingShape, shape child or NULL. Otherwise, NULL. */
     void *previousShape;
 };
@@ -1588,12 +1602,11 @@ js::UnmarkGrayGCThingRecursively(void *t
     JS_ASSERT(kind != JSTRACE_SHAPE);
 
     if (!GCThingIsMarkedGray(thing))
         return;
 
     UnmarkGrayGCThing(thing);
 
     JSRuntime *rt = static_cast<Cell *>(thing)->compartment()->rt;
-    UnmarkGrayTracer trc;
-    JS_TracerInit(&trc, rt, UnmarkGrayChildren);
+    UnmarkGrayTracer trc(rt);
     JS_TraceChildren(&trc, thing, kind);
 }
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -37,17 +37,18 @@ IonBuilder::IonBuilder(JSContext *cx, Te
     cx(cx),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
-    lazyArguments_(NULL)
+    lazyArguments_(NULL),
+    callee_(NULL)
 {
     script_.init(info->script());
     pc = info->startPC();
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
@@ -569,44 +570,50 @@ IonBuilder::initParameters()
     return true;
 }
 
 bool
 IonBuilder::initScopeChain()
 {
     MInstruction *scope = NULL;
 
+    // Add callee, it will be removed if it is not used by neither the scope
+    // chain nor the function body.
+    JSFunction *fun = info().fun();
+    if (fun) {
+        JS_ASSERT(!callee_);
+        callee_ = MCallee::New();
+        current->add(callee_);
+    }
+
     // If the script doesn't use the scopechain, then it's already initialized
     // from earlier.
     if (!script()->analysis()->usesScopeChain())
         return true;
 
     // The scope chain is only tracked in scripts that have NAME opcodes which
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
     if (!script()->compileAndGo)
         return abort("non-CNG global scripts are not supported");
 
-    if (JSFunction *fun = info().fun()) {
-        MCallee *callee = MCallee::New();
-        current->add(callee);
-
-        scope = MFunctionEnvironment::New(callee);
+    if (fun) {
+        scope = MFunctionEnvironment::New(callee_);
         current->add(scope);
 
         // This reproduce what is done in CallObject::createForFunction
         if (fun->isHeavyweight()) {
             if (fun->isNamedLambda()) {
-                scope = createDeclEnvObject(callee, scope);
+                scope = createDeclEnvObject(callee_, scope);
                 if (!scope)
                     return false;
             }
 
-            scope = createCallObject(callee, scope);
+            scope = createCallObject(callee_, scope);
             if (!scope)
                 return false;
         }
     } else {
         scope = MConstant::New(ObjectValue(script()->global()));
         current->add(scope);
     }
 
@@ -1029,22 +1036,19 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_NOT:
         return jsop_not();
 
       case JSOP_THIS:
         return jsop_this();
 
       case JSOP_CALLEE:
-      {
-        MCallee *callee = MCallee::New();
-        current->add(callee);
-        current->push(callee);
-        return callee;
-      }
+        JS_ASSERT(callee_);
+        current->push(callee_);
+        return true;
 
       case JSOP_GETPROP:
       case JSOP_CALLPROP:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
         return jsop_getprop(name);
       }
 
@@ -6651,31 +6655,16 @@ IonBuilder::jsop_lambda(JSFunction *fun)
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
-IonBuilder::jsop_deflocalfun(uint32_t local, JSFunction *fun)
-{
-    JS_ASSERT(script()->analysis()->usesScopeChain());
-
-    MLambda *ins = MLambda::New(current->scopeChain(), fun);
-    current->add(ins);
-    current->push(ins);
-
-    current->setLocal(local);
-    current->pop();
-
-    return resumeAfter(ins);
-}
-
-bool
 IonBuilder::jsop_defvar(uint32_t index)
 {
     JS_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST);
 
     PropertyName *name = script()->getName(index);
 
     // Bake in attrs.
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -370,17 +370,16 @@ class IonBuilder : public MIRGenerator
     bool jsop_delprop(HandlePropertyName name);
     bool jsop_newarray(uint32_t count);
     bool jsop_newobject(HandleObject baseObj);
     bool jsop_initelem_array();
     bool jsop_initprop(HandlePropertyName name);
     bool jsop_regexp(RegExpObject *reobj);
     bool jsop_object(JSObject *obj);
     bool jsop_lambda(JSFunction *fun);
-    bool jsop_deflocalfun(uint32_t local, JSFunction *fun);
     bool jsop_this();
     bool jsop_typeof();
     bool jsop_toid();
     bool jsop_iter(uint8_t flags);
     bool jsop_iternext();
     bool jsop_itermore();
     bool jsop_iterend();
     bool jsop_in();
@@ -507,18 +506,21 @@ class IonBuilder : public MIRGenerator
     // True if script->failedBoundsCheck is set for the current script or
     // an outer script.
     bool failedBoundsCheck_;
 
     // True if script->failedShapeGuard is set for the current script or
     // an outer script.
     bool failedShapeGuard_;
 
-    // If this script can use a lazy arguments object, it wil be pre-created
+    // If this script can use a lazy arguments object, it will be pre-created
     // here.
     MInstruction *lazyArguments_;
+
+    // If the script use a callee, it will be retrieved in the first basic
+    // block.
+    MCallee *callee_;
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_bytecode_analyzer_h__
-
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -1354,45 +1354,48 @@ js::ion::SetPropertyCache(JSContext *cx,
     IonScript *ion = script->ion;
     IonCacheSetProperty &cache = ion->getCache(cacheIndex).toSetProperty();
     RootedPropertyName name(cx, cache.name());
     RootedId id(cx, AtomToId(name));
     RootedShape shape(cx);
     RootedObject holder(cx);
 
     bool inlinable = IsPropertyInlineable(obj, cache);
+    bool addedSetterStub = false;
     if (inlinable) {
         RootedShape shape(cx);
         if (IsPropertySetInlineable(cx, obj, id, &shape)) {
             cache.incrementStubCount();
             if (!cache.attachNativeExisting(cx, ion, obj, shape))
                 return false;
+            addedSetterStub = true;
         } else {
             RootedObject holder(cx);
             if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape))
                 return false;
 
             if (IsPropertySetterCallInlineable(cx, obj, holder, id, shape)) {
                 cache.incrementStubCount();
                 if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr))
                     return false;
+                addedSetterStub = true;
             }
         }
     }
 
     uint32_t oldSlots = obj->numDynamicSlots();
     RootedShape oldShape(cx, obj->lastProperty());
 
     // Set/Add the property on the object, the inlined cache are setup for the next execution.
     if (!SetProperty(cx, obj, name, value, cache.strict(), isSetName))
         return false;
 
     // The property did not exists before, now we can try again to inline the
     // procedure which is adding the property.
-    if (inlinable && IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) {
+    if (inlinable && !addedSetterStub && IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) {
         RootedShape newShape(cx, obj->lastProperty());
         cache.incrementStubCount();
         if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape))
             return false;
     }
 
     return true;
 }
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -681,16 +681,17 @@ class MParameter : public MNullaryInstru
 };
 
 class MCallee : public MNullaryInstruction
 {
   public:
     MCallee()
     {
         setResultType(MIRType_Object);
+        setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Callee)
 
     bool congruentTo(MDefinition * const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
--- a/js/src/ion/arm/Assembler-arm.h
+++ b/js/src/ion/arm/Assembler-arm.h
@@ -704,38 +704,38 @@ class DtrOffImm : public DtrOff
     }
 };
 
 class DtrOffReg : public DtrOff
 {
     // These are designed to be called by a constructor of a subclass.
     // Constructing the necessary RIS/RRS structures are annoying
   protected:
-    DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm)
-      : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()))
+    DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm, IsUp_ iu = IsUp)
+      : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()), iu)
     { }
 
-    DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg)
-      : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()))
+    DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg, IsUp_ iu = IsUp)
+      : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()), iu)
     { }
 };
 
 class DtrRegImmShift : public DtrOffReg
 {
   public:
-    DtrRegImmShift(Register rn, ShiftType type, uint32_t shift)
-      : DtrOffReg(rn, type, datastore::RIS(shift))
+    DtrRegImmShift(Register rn, ShiftType type, uint32_t shift, IsUp_ iu = IsUp)
+      : DtrOffReg(rn, type, datastore::RIS(shift), iu)
     { }
 };
 
 class DtrRegRegShift : public DtrOffReg
 {
   public:
-    DtrRegRegShift(Register rn, ShiftType type, Register rs)
-      : DtrOffReg(rn, type, datastore::RRS(rs.code()))
+    DtrRegRegShift(Register rn, ShiftType type, Register rs, IsUp_ iu = IsUp)
+      : DtrOffReg(rn, type, datastore::RRS(rs.code()), iu)
     { }
 };
 
 // we will frequently want to bundle a register with its offset so that we have
 // an "operand" to a load instruction.
 class DTRAddr
 {
     uint32_t data;
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -2640,16 +2640,22 @@ MacroAssemblerARM::ma_call(void *dest)
 
 void
 MacroAssemblerARMCompat::breakpoint()
 {
     as_bkpt();
 }
 
 void
+MacroAssemblerARMCompat::breakpoint(Condition cc)
+{
+    ma_ldr(DTRAddr(r12, DtrRegImmShift(r12, LSL, 0, IsDown)), r12, Offset, cc);
+}
+
+void
 MacroAssemblerARMCompat::setupABICall(uint32_t args)
 {
     JS_ASSERT(!inCall_);
     inCall_ = true;
     args_ = args;
     passedArgs_ = 0;
 #ifdef JS_CPU_ARM_HARDFP
     usedIntSlots_ = 0;
@@ -2765,21 +2771,18 @@ void
 MacroAssemblerARMCompat::passABIArg(const FloatRegister &freg)
 {
     passABIArg(MoveOperand(freg));
 }
 
 void MacroAssemblerARMCompat::checkStackAlignment()
 {
 #ifdef DEBUG
-    Label good;
     ma_tst(sp, Imm32(StackAlignment - 1));
-    ma_b(&good, Equal);
-    breakpoint();
-    bind(&good);
+    breakpoint(Equal);
 #endif
 }
 
 void
 MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust)
 {
     JS_ASSERT(inCall_);
 #ifdef JS_CPU_ARM_HARDFP
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -1025,16 +1025,18 @@ class MacroAssemblerARMCompat : public M
     void addPtr(Imm32 imm, const Address &dest);
     void addPtr(ImmWord imm, const Register dest) {
         addPtr(Imm32(imm.value), dest);
     }
 
     void setStackArg(const Register &reg, uint32_t arg);
 
     void breakpoint();
+    // conditional breakpoint
+    void breakpoint(Condition cc);
 
     void compareDouble(FloatRegister lhs, FloatRegister rhs);
     void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
                       Label *label);
 
     void checkStackAlignment();
 
     void rshiftPtr(Imm32 imm, Register dest) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug819865.js
@@ -0,0 +1,2 @@
+(function x() (x == x))();
+
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -138,17 +138,17 @@ class WeakMap : public HashMap<Key, Valu
             return false;
         gc::Mark(trc, x, "WeakMap entry");
         JS_ASSERT(gc::IsMarked(x));
         return true;
     }
 
     void nonMarkingTrace(JSTracer *trc) {
         for (Range r = Base::all(); !r.empty(); r.popFront())
-            markValue(trc, &r.front().value);
+            gc::Mark(trc, &r.front().value, "WeakMap entry");
     }
 
     bool keyNeedsMark(JSObject *key) {
         if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
             JSObject *delegate = op(key);
             /*
              * Check if the delegate is marked with any color to properly handle
              * gray marking when the key's delegate is black and the map is
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -2717,19 +2717,17 @@ ChooseScaleAndSetTransform(FrameLayerBui
     // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't
     // matter.
     nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame);
     nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
     offset = nsIntPoint(
         int32_t(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale),
         int32_t(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale));
   }
-  transform = gfx3DMatrix::Translation(aIncomingScale.mOffset.x, aIncomingScale.mOffset.y, 0) * 
-              transform * 
-              gfx3DMatrix::Translation(offset.x, offset.y, 0);
+  transform = transform * gfx3DMatrix::Translation(offset.x + aIncomingScale.mOffset.x, offset.y + aIncomingScale.mOffset.y, 0);
 
 
   bool canDraw2D = transform.CanDraw2D(&transform2d);
   gfxSize scale;
   bool isRetained = aLayer->Manager()->IsWidgetLayerManager();
   // Only fiddle with scale factors for the retaining layer manager, since
   // it only matters for retained layers
   // XXX Should we do something for 3D transforms?
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -172,16 +172,22 @@ SHARED_LIBRARY_LIBS     += \
 endif
 
 ifdef MOZ_WEBM
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/webm/$(LIB_PREFIX)gkconwebm_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
+ifdef MOZ_WMF
+SHARED_LIBRARY_LIBS 	+= \
+	$(DEPTH)/content/media/wmf/$(LIB_PREFIX)gkconwmf_s.$(LIB_SUFFIX) \
+	$(NULL)
+endif
+
 ifdef MOZ_WAVE
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/wave/$(LIB_PREFIX)gkconwave_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
 ifdef MOZ_GSTREAMER
 SHARED_LIBRARY_LIBS 	+= \
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -74,16 +74,20 @@
 
 #include "nsHTMLEditor.h"
 #include "nsTextServicesDocument.h"
 
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
+
 #ifdef MOZ_SYDNEYAUDIO
 #include "AudioStream.h"
 #endif
 
 #include "nsError.h"
 
 #include "nsCycleCollector.h"
 #include "nsJSEnvironment.h"
@@ -329,25 +333,29 @@ nsLayoutStatics::Shutdown()
   nsDOMClassInfo::ShutDown();
   nsListControlFrame::Shutdown();
   nsXBLWindowKeyHandler::ShutDown();
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_MEDIA_PLUGINS
-  MediaPluginHost::Shutdown();  
+  MediaPluginHost::Shutdown();
 #endif
 
 #ifdef MOZ_SYDNEYAUDIO
   AudioStream::ShutdownLibrary();
 #endif
 
+#ifdef MOZ_WMF
+  WMFDecoder::UnloadDLLs();
+#endif
+
   nsCORSListenerProxy::Shutdown();
-  
+
   nsIPresShell::ReleaseStatics();
 
   nsTreeSanitizer::ReleaseStatics();
 
   nsHtml5Module::ReleaseStatics();
 
   nsRegion::ShutdownStatic();
 
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -8,17 +8,16 @@
 #ifndef nsBulletFrame_h___
 #define nsBulletFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsFrame.h"
 #include "nsStyleContext.h"
 
 #include "imgIRequest.h"
-#include "imgIDecoderObserver.h"
 #include "imgINotificationObserver.h"
 
 class imgRequestProxy;
 
 #define BULLET_FRAME_IMAGE_LOADING NS_FRAME_STATE_BIT(63)
 #define BULLET_FRAME_HAS_FONT_INFLATION NS_FRAME_STATE_BIT(62)
 
 class nsBulletFrame;
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -270,17 +270,17 @@ NS_QUERYFRAME_HEAD(nsObjectFrame)
   NS_QUERYFRAME_ENTRY(nsObjectFrame)
   NS_QUERYFRAME_ENTRY(nsIObjectFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsObjectFrameSuper)
 
 #ifdef ACCESSIBILITY
 a11y::AccType
 nsObjectFrame::AccessibleType()
 {
-  return a11y::ePlugin;
+  return a11y::ePluginType;
 }
 
 #ifdef XP_WIN
 NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort)
 {
   *aPort = (HWND) mInstanceOwner->GetPluginPortFromWidget();
   return NS_OK;
 }
--- a/layout/xul/base/public/nsIPopupBoxObject.idl
+++ b/layout/xul/base/public/nsIPopupBoxObject.idl
@@ -5,17 +5,17 @@
 
 #include "nsIBoxObject.idl"
 
 interface nsIDOMElement;
 interface nsIDOMNode;
 interface nsIDOMEvent;
 interface nsIDOMClientRect;
 
-[scriptable, uuid(ACCEA57B-C3D8-4B6E-9101-90F04EE9DEA0)]
+[scriptable, uuid(DF60BA02-005B-4F61-AB1C-5632D0779DB4)]
 interface nsIPopupBoxObject : nsISupports
 {
   /**
    *  This method is deprecated. Use openPopup or openPopupAtScreen instead.
    */
   void showPopup(in nsIDOMElement srcContent, in nsIDOMElement popupContent,
                  in long xpos, in long ypos,
                  in wstring popupType, in wstring anchorAlignment, 
@@ -161,15 +161,20 @@ interface nsIPopupBoxObject : nsISupport
    * Move an open popup to the given anchor position. The arguments have the same
    * meaning as the corresponding argument to openPopup. This method has no effect
    * on popups that are not open.
    */
   void moveToAnchor(in nsIDOMElement anchorElement,
                     in AString position,
                     in long x, in long y,
                     in boolean attributesOverride);
+
+  /** Returns the alignment position where the popup has appeared relative to its
+   *  anchor node or point, accounting for any flipping that occurred.
+   */
+  readonly attribute AString alignmentPosition;
 };
 
 %{C++
 nsresult
 NS_NewPopupBoxObject(nsIBoxObject** aResult);
 
 %}
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -77,16 +77,17 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPr
   :nsBoxFrame(aShell, aContext),
   mCurrentMenu(nullptr),
   mPrefSize(-1, -1),
   mLastClientOffset(0, 0),
   mPopupType(ePopupTypePanel),
   mPopupState(ePopupClosed),
   mPopupAlignment(POPUPALIGNMENT_NONE),
   mPopupAnchor(POPUPALIGNMENT_NONE),
+  mPosition(POPUPPOSITION_UNKNOWN),
   mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
   mFlipBoth(false),
   mIsOpenChanged(false),
   mIsContextMenu(false),
   mAdjustOffsetForContextMenu(false),
   mGeneratedChildren(false),
   mMenuCanOverlapOSBar(false),
   mShouldAutoPosition(true),
@@ -529,16 +530,18 @@ nsMenuPopupFrame::InitPositionFromAnchor
   else if (aAlign.EqualsLiteral("topright"))
     mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
   else if (aAlign.EqualsLiteral("bottomleft"))
     mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
   else if (aAlign.EqualsLiteral("bottomright"))
     mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
   else
     mPopupAlignment = POPUPALIGNMENT_NONE;
+
+  mPosition = POPUPPOSITION_UNKNOWN;
 }
 
 void
 nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
                                   nsIContent* aTriggerContent,
                                   const nsAString& aPosition,
                                   int32_t aXPos, int32_t aYPos,
                                   bool aAttributesOverride)
@@ -546,16 +549,17 @@ nsMenuPopupFrame::InitializePopup(nsICon
   EnsureWidget();
 
   mPopupState = ePopupShowing;
   mAnchorContent = aAnchorContent;
   mTriggerContent = aTriggerContent;
   mXPos = aXPos;
   mYPos = aYPos;
   mAdjustOffsetForContextMenu = false;
+  mPosition = POPUPPOSITION_UNKNOWN;
 
   // if aAttributesOverride is true, then the popupanchor, popupalign and
   // position attributes on the <popup> override those values passed in.
   // If false, those attributes are only used if the values passed in are empty
   if (aAnchorContent) {
     nsAutoString anchor, align, position, flip;
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, anchor);
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, align);
@@ -581,52 +585,62 @@ nsMenuPopupFrame::InitializePopup(nsICon
     // if there is a space in the position, assume it is the anchor and
     // alignment as two separate tokens.
     if (spaceIdx >= 0) {
       InitPositionFromAnchorAlign(Substring(position, 0, spaceIdx), Substring(position, spaceIdx + 1));
     }
     else if (position.EqualsLiteral("before_start")) {
       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
       mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
+      mPosition = POPUPPOSITION_BEFORESTART;
     }
     else if (position.EqualsLiteral("before_end")) {
       mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
       mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
+      mPosition = POPUPPOSITION_BEFOREEND;
     }
     else if (position.EqualsLiteral("after_start")) {
       mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
+      mPosition = POPUPPOSITION_AFTERSTART;
     }
     else if (position.EqualsLiteral("after_end")) {
       mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
       mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
+      mPosition = POPUPPOSITION_AFTEREND;
     }
     else if (position.EqualsLiteral("start_before")) {
       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
       mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
+      mPosition = POPUPPOSITION_STARTBEFORE;
     }
     else if (position.EqualsLiteral("start_after")) {
       mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
       mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
+      mPosition = POPUPPOSITION_STARTAFTER;
     }
     else if (position.EqualsLiteral("end_before")) {
       mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
+      mPosition = POPUPPOSITION_ENDBEFORE;
     }
     else if (position.EqualsLiteral("end_after")) {
       mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
       mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
+      mPosition = POPUPPOSITION_ENDAFTER;
     }
     else if (position.EqualsLiteral("overlap")) {
       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
+      mPosition = POPUPPOSITION_OVERLAP;
     }
     else if (position.EqualsLiteral("after_pointer")) {
       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
+      mPosition = POPUPPOSITION_AFTERPOINTER;
       // XXXndeakin this is supposed to anchor vertically after, but with the
       // horizontal position as the mouse pointer.
       mYPos += 21;
     }
     else {
       InitPositionFromAnchorAlign(anchor, align);
     }
   }
@@ -860,18 +874,18 @@ nsMenuPopupFrame::GetRootViewForPopup(ns
 nsPoint
 nsMenuPopupFrame::AdjustPositionForAnchorAlign(nsRect& anchorRect,
                                                FlipStyle& aHFlip, FlipStyle& aVFlip)
 {
   // flip the anchor and alignment for right-to-left
   int8_t popupAnchor(mPopupAnchor);
   int8_t popupAlign(mPopupAlignment);
   if (IsDirectionRTL()) {
-    // no need to flip the centered anchor types
-    if (popupAnchor < POPUPALIGNMENT_LEFTCENTER) {
+    // no need to flip the centered anchor types vertically
+    if (popupAnchor <= POPUPALIGNMENT_LEFTCENTER) {
       popupAnchor = -popupAnchor;
     }
     popupAlign = -popupAlign;
   }
 
   // first, determine at which corner of the anchor the popup should appear
   nsPoint pnt;
   switch (popupAnchor) {
@@ -941,36 +955,39 @@ nsMenuPopupFrame::AdjustPositionForAncho
   // vertically as well.
   // If we are flipping in both directions, we want to set a flip style both
   // horizontally and vertically. However, we want to flip on the inside edge
   // of the anchor. Consider the example of a typical dropdown menu.
   // Vertically, we flip the popup on the outside edges of the anchor menu,
   // however horizontally, we want to to use the inside edges so the popup
   // still appears underneath the anchor menu instead of floating off the
   // side of the menu.
-  if (popupAnchor >= POPUPALIGNMENT_LEFTCENTER) {
-    if (popupAnchor == POPUPALIGNMENT_LEFTCENTER ||
-        popupAnchor == POPUPALIGNMENT_RIGHTCENTER) {
+  switch (popupAnchor) {
+    case POPUPALIGNMENT_LEFTCENTER:
+    case POPUPALIGNMENT_RIGHTCENTER:
       aHFlip = FlipStyle_Outside;
       aVFlip = FlipStyle_Inside;
-    }
-    else {
+      break;
+    case POPUPALIGNMENT_TOPCENTER:
+    case POPUPALIGNMENT_BOTTOMCENTER:
       aHFlip = FlipStyle_Inside;
       aVFlip = FlipStyle_Outside;
+      break;
+    default:
+    {
+      FlipStyle anchorEdge = mFlipBoth ? FlipStyle_Inside : FlipStyle_None;
+      aHFlip = (popupAnchor == -popupAlign) ? FlipStyle_Outside : anchorEdge;
+      if (((popupAnchor > 0) == (popupAlign > 0)) ||
+          (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT))