Merge mozilla-inbound to mozilla-central on a CLOSED TREE
authorEd Morley <emorley@mozilla.com>
Fri, 14 Dec 2012 15:28:22 +0000
changeset 116051 50d8f411d3059f95c4242febc114852d3c836222
parent 115928 8a30e07815ff4aa3d1ff2fd2d21daf93e251fa7d (current diff)
parent 116050 f255406bb4aa578bcf70364c5b21af473f4bf637 (diff)
child 116052 72cc10ffa8e2f31964f303925b1cc07abfbf4e54
child 116072 9d1bb565ab3ad8df0593d1fbbe0d101116da219b
child 127148 b95c391e06c115e57eb31b5d62ac1621aa75c155
push id24034
push useremorley@mozilla.com
push dateFri, 14 Dec 2012 15:28:57 +0000
treeherdermozilla-central@50d8f411d305 [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 mozilla-inbound to mozilla-central on a CLOSED TREE
browser/base/content/browser.js
browser/devtools/debugger/test/Makefile.in
browser/devtools/debugger/test/browser_dbg_leaktest.js
dom/apps/src/Webapps.jsm
dom/interfaces/css/nsIDOMCSSValueList.idl
layout/tables/nsITableLayout.h
profile/public/nsIProfile.idl
profile/public/nsIProfileChangeStatus.idl
security/manager/ssl/src/PublicSSL.h
security/manager/ssl/src/SharedSSLState.cpp
security/manager/ssl/src/SharedSSLState.h
security/manager/ssl/tests/unit/xpcshell.ini
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -25,22 +25,23 @@
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLTableCellElement.h"
 #include "nsIDOMHTMLTableElement.h"
 #include "nsIDOMHTMLTableRowElement.h"
 #include "nsIDOMHTMLTableSectionElem.h"
 #include "nsIDocument.h"
 #include "nsIMutableArray.h"
 #include "nsIPresShell.h"
-#include "nsITableLayout.h"
 #include "nsITableCellLayout.h"
 #include "nsFrameSelection.h"
 #include "nsError.h"
 #include "nsArrayUtils.h"
 #include "nsComponentManagerUtils.h"
+#include "nsTableCellFrame.h"
+#include "nsTableOuterFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableCellAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -466,62 +467,45 @@ HTMLTableAccessible::Summary(nsString& a
 
   if (table)
     table->GetSummary(aSummary);
 }
 
 uint32_t
 HTMLTableAccessible::ColCount()
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout)
-    return 0;
-
-  int32_t rowCount = 0, colCount = 0;
-  tableLayout->GetTableSize(rowCount, colCount);
-  return colCount;
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  return tableFrame ? tableFrame->GetColCount() : 0;
 }
 
 uint32_t
 HTMLTableAccessible::RowCount()
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout)
-    return 0;
-
-  int32_t rowCount = 0, colCount = 0;
-  tableLayout->GetTableSize(rowCount, colCount);
-  return rowCount;
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  return tableFrame ? tableFrame->GetRowCount() : 0;
 }
 
 uint32_t
 HTMLTableAccessible::SelectedCellCount()
 {
-  nsITableLayout *tableLayout = GetTableLayout();
-  if (!tableLayout)
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return 0;
 
   uint32_t count = 0, rowCount = RowCount(), colCount = ColCount();
-
-  nsCOMPtr<nsIDOMElement> domElement;
-  int32_t startRowIndex = 0, startColIndex = 0,
-    rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool isSelected = false;
-
   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
-      nsresult rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
-                                               *getter_AddRefs(domElement),
-                                               startRowIndex, startColIndex,
-                                               rowSpan, colSpan,
-                                               actualRowSpan, actualColSpan,
-                                               isSelected);
+      nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+      if (!cellFrame || !cellFrame->IsSelected())
+        continue;
 
-      if (NS_SUCCEEDED(rv) && startRowIndex == rowIdx &&
-          startColIndex == colIdx && isSelected)
+      int32_t startRow = -1, startCol = -1;
+      cellFrame->GetRowIndex(startRow);
+      cellFrame->GetColIndex(startCol);
+      if (startRow == rowIdx && startCol == colIdx)
         count++;
     }
   }
 
   return count;
 }
 
 uint32_t
@@ -546,71 +530,57 @@ HTMLTableAccessible::SelectedRowCount()
       count++;
 
   return count;
 }
 
 void
 HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
 {
-  uint32_t rowCount = RowCount(), colCount = ColCount();
-
-  nsITableLayout *tableLayout = GetTableLayout();
-  if (!tableLayout) 
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return;
 
-  nsCOMPtr<nsIDOMElement> cellElement;
-  int32_t startRowIndex = 0, startColIndex = 0,
-    rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool isSelected = false;
-
+  uint32_t rowCount = RowCount(), colCount = ColCount();
   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
-      nsresult rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
-                                      *getter_AddRefs(cellElement),
-                                      startRowIndex, startColIndex,
-                                      rowSpan, colSpan,
-                                      actualRowSpan, actualColSpan,
-                                      isSelected);
+      nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+      if (!cellFrame || !cellFrame->IsSelected())
+        continue;
 
-      if (NS_SUCCEEDED(rv) && startRowIndex == rowIdx &&
-          startColIndex == colIdx && isSelected) {
-        nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
-        Accessible* cell = mDoc->GetAccessible(cellContent);
+      int32_t startCol = -1, startRow = -1;
+      cellFrame->GetRowIndex(startRow);
+      cellFrame->GetColIndex(startCol);
+      if (startRow != rowIdx || startCol != colIdx)
+        continue;
+
+      Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
         aCells->AppendElement(cell);
-      }
     }
   }
 }
 
 void
 HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
 {
-  nsITableLayout *tableLayout = GetTableLayout();
-  if (!tableLayout)
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return;
 
   uint32_t rowCount = RowCount(), colCount = ColCount();
-
-  nsCOMPtr<nsIDOMElement> domElement;
-  int32_t startRowIndex = 0, startColIndex = 0,
-    rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool isSelected = false;
-
   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
-      nsresult rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
-                                               *getter_AddRefs(domElement),
-                                               startRowIndex, startColIndex,
-                                               rowSpan, colSpan,
-                                               actualRowSpan, actualColSpan,
-                                               isSelected);
+      nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+      if (!cellFrame || !cellFrame->IsSelected())
+        continue;
 
-      if (NS_SUCCEEDED(rv) && startRowIndex == rowIdx &&
-          startColIndex == colIdx && isSelected)
+      int32_t startRow = -1, startCol = -1;
+      cellFrame->GetColIndex(startCol);
+      cellFrame->GetRowIndex(startRow);
+      if (startRow == rowIdx && startCol == colIdx)
         aCells->AppendElement(CellIndexAt(rowIdx, colIdx));
     }
   }
 }
 
 void
 HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
 {
@@ -625,118 +595,91 @@ HTMLTableAccessible::SelectedRowIndices(
 {
   uint32_t rowCount = RowCount();
   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++)
     if (IsRowSelected(rowIdx))
       aRows->AppendElement(rowIdx);
 }
 
 Accessible*
-HTMLTableAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
-{ 
-  nsCOMPtr<nsIDOMElement> cellElement;
-  GetCellAt(aRowIndex, aColumnIndex, *getter_AddRefs(cellElement));
-  if (!cellElement)
+HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx)
+{
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return nullptr;
 
-  nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
-  if (!cellContent)
-    return nullptr;
-
+  nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx);
   Accessible* cell = mDoc->GetAccessible(cellContent);
 
   // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may
   // return itself as a cell what makes Orca hang.
   return cell == this ? nullptr : cell;
 }
 
 int32_t
 HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
+    return -1;
 
-  int32_t index = -1;
-  tableLayout->GetIndexByRowAndColumn(aRowIdx, aColIdx, &index);
-  return index;
+  return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx);
 }
 
 int32_t
 HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout) 
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return -1;
 
   int32_t rowIdx = -1, colIdx = -1;
-  tableLayout->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
+  tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
   return colIdx;
 }
 
 int32_t
 HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout) 
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return -1;
 
   int32_t rowIdx = -1, colIdx = -1;
-  tableLayout->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
+  tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
   return rowIdx;
 }
 
 void
 HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx,
                                         int32_t* aColIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-
-  if (tableLayout)
-    tableLayout->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx);
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (tableFrame)
+    tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx);
 }
 
 uint32_t
 HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout)
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return 0;
 
-  nsCOMPtr<nsIDOMElement> domElement;
-  int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan;
-  bool isSelected;
-  int32_t columnExtent = 0;
-
-  DebugOnly<nsresult> rv = tableLayout->
-    GetCellDataAt(aRowIdx, aColIdx, *getter_AddRefs(domElement),
-                  startRowIndex, startColIndex, rowSpan, colSpan,
-                  actualRowSpan, columnExtent, isSelected);
-  NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get cell data");
-
-  return columnExtent;
+  return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx);
 }
 
 uint32_t
 HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx)
 {
-  nsITableLayout* tableLayout = GetTableLayout();
-  if (!tableLayout)
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return 0;
 
-  nsCOMPtr<nsIDOMElement> domElement;
-  int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualColSpan;
-  bool isSelected;
-  int32_t rowExtent = 0;
-
-  DebugOnly<nsresult> rv = tableLayout->
-    GetCellDataAt(aRowIdx, aColIdx, *getter_AddRefs(domElement),
-                  startRowIndex, startColIndex, rowSpan, colSpan,
-                  rowExtent, actualColSpan, isSelected);
-  NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get cell data");
-
-  return rowExtent;
+  return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx);
 }
 
 bool
 HTMLTableAccessible::IsColSelected(uint32_t aColIdx)
 {
   bool isSelected = false;
 
   uint32_t rowCount = RowCount();
@@ -762,30 +705,22 @@ HTMLTableAccessible::IsRowSelected(uint3
   }
 
   return isSelected;
 }
 
 bool
 HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
 {
-  nsITableLayout *tableLayout = GetTableLayout();
-  if (!tableLayout)
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
     return false;
 
-  nsCOMPtr<nsIDOMElement> domElement;
-  int32_t startRowIndex = 0, startColIndex = 0,
-          rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool isSelected = false;
-
-  tableLayout->GetCellDataAt(aRowIdx, aColIdx, *getter_AddRefs(domElement),
-                             startRowIndex, startColIndex, rowSpan, colSpan,
-                             actualRowSpan, actualColSpan, isSelected);
-
-  return isSelected;
+  nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx);
+  return cellFrame ? cellFrame->IsSelected() : false;
 }
 
 void
 HTMLTableAccessible::SelectRow(uint32_t aRowIdx)
 {
   nsresult rv =
     RemoveRowsOrColumnsFromSelection(aRowIdx,
                                      nsISelectionPrivate::TABLESELECTION_ROW,
@@ -825,120 +760,74 @@ HTMLTableAccessible::UnselectCol(uint32_
                                    false);
 }
 
 nsresult
 HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget)
 {
   bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
 
-  nsITableLayout *tableLayout = GetTableLayout();
-  NS_ENSURE_STATE(tableLayout);
-
-  nsCOMPtr<nsIDOMElement> cellElm;
-  int32_t startRowIdx, startColIdx, rowSpan, colSpan,
-    actualRowSpan, actualColSpan;
-  bool isSelected = false;
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
+    return NS_OK;
 
-  nsresult rv = NS_OK;
-  int32_t count = 0;
+  uint32_t count = 0;
   if (doSelectRow)
-    rv = GetColumnCount(&count);
+    count = ColCount();
   else
-    rv = GetRowCount(&count);
-
-  NS_ENSURE_SUCCESS(rv, rv);
+    count = RowCount();
 
   nsIPresShell* presShell(mDoc->PresShell());
   nsRefPtr<nsFrameSelection> tableSelection =
     const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
 
-  for (int32_t idx = 0; idx < count; idx++) {
+  for (uint32_t idx = 0; idx < count; idx++) {
     int32_t rowIdx = doSelectRow ? aIndex : idx;
     int32_t colIdx = doSelectRow ? idx : aIndex;
-    rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
-                                    *getter_AddRefs(cellElm),
-                                    startRowIdx, startColIdx,
-                                    rowSpan, colSpan,
-                                    actualRowSpan, actualColSpan,
-                                    isSelected);
-
-    if (NS_SUCCEEDED(rv) && !isSelected) {
-      nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
-      rv = tableSelection->SelectCellElement(cellContent);
+    nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+    if (cellFrame && !cellFrame->IsSelected()) {
+      nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent());
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(int32_t aIndex,
                                                       uint32_t aTarget,
                                                       bool aIsOuter)
 {
-  nsITableLayout *tableLayout = GetTableLayout();
-  NS_ENSURE_STATE(tableLayout);
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
+    return NS_OK;
 
   nsIPresShell* presShell(mDoc->PresShell());
   nsRefPtr<nsFrameSelection> tableSelection =
     const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
 
   bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
-  int32_t count = 0;
-  nsresult rv = doUnselectRow ? GetColumnCount(&count) : GetRowCount(&count);
-  NS_ENSURE_SUCCESS(rv, rv);
+  uint32_t count = doUnselectRow ? ColCount() : RowCount();
 
   int32_t startRowIdx = doUnselectRow ? aIndex : 0;
   int32_t endRowIdx = doUnselectRow ? aIndex : count - 1;
   int32_t startColIdx = doUnselectRow ? 0 : aIndex;
   int32_t endColIdx = doUnselectRow ? count - 1 : aIndex;
 
   if (aIsOuter)
     return tableSelection->RestrictCellsToSelection(mContent,
                                                     startRowIdx, startColIdx,
                                                     endRowIdx, endColIdx);
 
   return tableSelection->RemoveCellsFromSelection(mContent,
                                                   startRowIdx, startColIdx,
                                                   endRowIdx, endColIdx);
 }
 
-nsITableLayout*
-HTMLTableAccessible::GetTableLayout()
-{
-  nsIFrame *frame = mContent->GetPrimaryFrame();
-  if (!frame)
-    return nullptr;
-
-  nsITableLayout *tableLayout = do_QueryFrame(frame);
-  return tableLayout;
-}
-
-nsresult
-HTMLTableAccessible::GetCellAt(int32_t aRowIndex, int32_t aColIndex,
-                               nsIDOMElement*& aCell)
-{
-  int32_t startRowIndex = 0, startColIndex = 0,
-          rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool isSelected;
-
-  nsITableLayout *tableLayout = GetTableLayout();
-  NS_ENSURE_STATE(tableLayout);
-
-  nsresult rv = tableLayout->
-    GetCellDataAt(aRowIndex, aColIndex, aCell, startRowIndex, startColIndex,
-                  rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected);
-
-  if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND)
-    return NS_ERROR_INVALID_ARG;
-  return rv;
-}
-
 void
 HTMLTableAccessible::Description(nsString& aDescription)
 {
   // Helpful for debugging layout vs. data tables
   aDescription.Truncate();
   Accessible::Description(aDescription);
   if (!aDescription.IsEmpty())
     return;
@@ -1135,26 +1024,24 @@ HTMLTableAccessible::IsProbablyLayoutTab
   // Check for many columns
   if (columns >= 5) {
     RETURN_LAYOUT_ANSWER(false, ">=5 columns");
   }
 
   // Now we know there are 2-4 columns and 2 or more rows
   // Check to see if there are visible borders on the cells
   // XXX currently, we just check the first cell -- do we really need to do more?
-  nsCOMPtr<nsIDOMElement> cellElement;
-  nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  if (!tableFrame)
+    RETURN_LAYOUT_ANSWER(false, "table with no frame!");
 
-  nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
-  NS_ENSURE_TRUE(cellContent, false);
-  nsIFrame *cellFrame = cellContent->GetPrimaryFrame();
-  if (!cellFrame) {
-    RETURN_LAYOUT_ANSWER(false, "Could not get frame for cellContent");
-  }
+  nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
+  if (!cellFrame)
+    RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
+
   nsMargin border;
   cellFrame->GetBorder(border);
   if (border.top && border.bottom && border.left && border.right) {
     RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
   }
 
   /**
    * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
--- a/accessible/src/html/HTMLTableAccessible.h
+++ b/accessible/src/html/HTMLTableAccessible.h
@@ -161,29 +161,16 @@ public:
   // Accessible
   virtual TableAccessible* AsTable() { return this; }
   virtual void Description(nsString& aDescription);
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() MOZ_OVERRIDE;
   virtual Relation RelationByType(uint32_t aRelationType);
 
-  // HTMLTableAccessible
-
-  /**
-   * Retun cell element at the given row and column index.
-   */
-  nsresult GetCellAt(int32_t aRowIndex, int32_t aColIndex,
-                     nsIDOMElement* &aCell);
-
-  /**
-   * Return nsITableLayout for the frame of the accessible table.
-   */
-  nsITableLayout* GetTableLayout();
-
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
   virtual void CacheChildren();
 
   // HTMLTableAccessible
 
   /**
--- a/accessible/src/html/Makefile.in
+++ b/accessible/src/html/Makefile.in
@@ -34,16 +34,17 @@ include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES = \
   -I$(srcdir)/../base \
   -I$(srcdir)/../generic \
   -I$(srcdir)/../xpcom \
   -I$(srcdir)/../../../content/base/src \
   -I$(srcdir)/../../../content/html/content/src \
   -I$(srcdir)/../../../layout/generic \
+  -I$(srcdir)/../../../layout/tables \
   -I$(srcdir)/../../../layout/xul/base/src \
   $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 LOCAL_INCLUDES += \
   -I$(srcdir)/../atk \
   $(NULL)
 else
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -141,18 +141,18 @@ libs::
 endif
 
 libs:: $(srcdir)/profile/prefs.js
 	$(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/defaults/profile
 
 ifndef LIBXUL_SDK
 # channel-prefs.js is handled separate from other prefs due to bug 756325
 libs:: $(srcdir)/profile/channel-prefs.js
-	$(NSINSTALL) -D $(FINAL_TARGET)/defaults/pref
-	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(ACDEFINES) $^ > $(FINAL_TARGET)/defaults/pref/channel-prefs.js
+	$(NSINSTALL) -D $(DIST)/bin/defaults/pref
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(ACDEFINES) $^ > $(DIST)/bin/defaults/pref/channel-prefs.js
 endif
 
 libs:: $(srcdir)/blocklist.xml
 	$(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 
 MAC_APP_NAME = $(MOZ_APP_DISPLAYNAME)
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1088,36 +1088,32 @@ var SocialSidebar = {
     // Also set the command "checked" state accordingly.
     let hideSidebar = !this.canShow || !this.opened;
     let broadcaster = document.getElementById("socialSidebarBroadcaster");
     broadcaster.hidden = hideSidebar;
     command.setAttribute("checked", !hideSidebar);
 
     let sbrowser = document.getElementById("social-sidebar-browser");
 
-    if (Social.provider)
-      sbrowser.setAttribute("origin", Social.provider.origin);
-    else
-      sbrowser.removeAttribute("origin");
-
     if (hideSidebar) {
       sbrowser.removeEventListener("load", SocialSidebar._loadListener, true);
       this.setSidebarVisibilityState(false);
       // If we've been disabled, unload the sidebar content immediately;
       // if the sidebar was just toggled to invisible, wait a timeout
       // before unloading.
       if (!this.canShow) {
         this.unloadSidebar();
       } else {
         this._unloadTimeoutId = setTimeout(
           this.unloadSidebar,
           Services.prefs.getIntPref("social.sidebar.unload_timeout_ms")
         );
       }
     } else {
+      sbrowser.setAttribute("origin", Social.provider.origin);
       if (Social.provider.errorState == "frameworker-error") {
         SocialSidebar.setSidebarErrorMessage("frameworker-error");
         return;
       }
 
       // Make sure the right sidebar URL is loaded
       if (sbrowser.getAttribute("src") != Social.provider.sidebarURL) {
         sbrowser.setAttribute("src", Social.provider.sidebarURL);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3489,27 +3489,26 @@ function OpenBrowserWindow(options)
 
   var charsetArg = new String();
   var handler = Components.classes["@mozilla.org/browser/clh;1"]
                           .getService(Components.interfaces.nsIBrowserHandler);
   var defaultArgs = handler.defaultArgs;
   var wintype = document.documentElement.getAttribute('windowtype');
 
   var extraFeatures = "";
-  var forcePrivate = false;
 #ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
-  forcePrivate = typeof options == "object" && "private" in options && options.private;
+  if (typeof options == "object" && options.private) {
 #else
-  forcePrivate = gPrivateBrowsingUI.privateBrowsingEnabled;
+  if (gPrivateBrowsingUI.privateBrowsingEnabled) {
 #endif
-
-  if (forcePrivate) {
     extraFeatures = ",private";
     // Force the new window to load about:privatebrowsing instead of the default home page
     defaultArgs = "about:privatebrowsing";
+  } else {
+    extraFeatures = ",non-private";
   }
 
   // if and only if the current window is a browser window and it has a document with a character
   // set, then extract the current charset menu setting from the current document and use it to
   // initialize the new browser window...
   var win;
   if (window && (wintype == "navigator:browser") && window.content && window.content.document)
   {
--- a/browser/base/content/test/browser_pluginnotification.js
+++ b/browser/base/content/test/browser_pluginnotification.js
@@ -987,20 +987,22 @@ function test23() {
 
   // Activate
   objLoadingContent.playPlugin();
   is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 23, plugin should have started");
   ok(pluginNode.activated, "Test 23, plugin should be activated");
 
   // Reload plugin (this may need RunSoon() in the future when plugins change state asynchronously)
   pluginNode.type = null;
-  pluginNode.src = pluginNode.src; // We currently don't properly change state just on type change, bug 767631
+  // We currently don't properly change state just on type change,
+  // so rebind the plugin to tree. bug 767631
+  pluginNode.parentNode.appendChild(pluginNode);
   is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, plugin should be unloaded");
   pluginNode.type = "application/x-test";
-  pluginNode.src = pluginNode.src;
+  pluginNode.parentNode.appendChild(pluginNode);
   is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, Plugin should not have activated");
   is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 23, Plugin should be click-to-play");
   ok(!pluginNode.activated, "Test 23, plugin node should not be activated");
 
   prepareTest(test24a, gHttpTestRoot + "plugin_test.html");
 }
 
 // Test that "always allow"-ing a plugin will not allow it when it becomes
--- a/browser/base/content/test/browser_private_browsing_window.js
+++ b/browser/base/content/test/browser_private_browsing_window.js
@@ -1,11 +1,24 @@
 // Make sure that we can open private browsing windows
 
 function test() {
+  waitForExplicitFinish();
   var nonPrivateWin = OpenBrowserWindow();
   ok(!PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), "OpenBrowserWindow() should open a normal window");
   nonPrivateWin.close();
+
   var privateWin = OpenBrowserWindow({private: true});
   ok(PrivateBrowsingUtils.isWindowPrivate(privateWin), "OpenBrowserWindow({private: true}) should open a private window");
-  privateWin.close();
+
+  nonPrivateWin = OpenBrowserWindow({private: false});
+  ok(!PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), "OpenBrowserWindow({private: false}) should open a normal window");
+  nonPrivateWin.close();
+
+  whenDelayedStartupFinished(privateWin, function() {
+    nonPrivateWin = privateWin.OpenBrowserWindow({private: false});
+    ok(!PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), "privateWin.OpenBrowserWindow({private: false}) should open a normal window");
+    nonPrivateWin.close();
+    privateWin.close();
+    finish();
+  });
 }
 
--- a/browser/base/content/test/social/browser_social_sidebar.js
+++ b/browser/base/content/test/social/browser_social_sidebar.js
@@ -23,36 +23,67 @@ function doTest(finishcb) {
   let sidebar = document.getElementById("social-sidebar-box");
   let browser = sidebar.firstChild;
 
   function checkShown(shouldBeShown) {
     is(command.getAttribute("checked"), shouldBeShown ? "true" : "false",
        "toggle command should be " + (shouldBeShown ? "checked" : "unchecked"));
     is(sidebar.hidden, !shouldBeShown,
        "sidebar should be " + (shouldBeShown ? "visible" : "hidden"));
-    is(Services.prefs.getBoolPref("social.sidebar.open"), shouldBeShown,
-       "sidebar open pref should be " + shouldBeShown);
-    if (shouldBeShown)
+    // The sidebar.open pref only reflects the actual state of the sidebar
+    // when social is enabled.
+    if (Social.enabled)
+      is(Services.prefs.getBoolPref("social.sidebar.open"), shouldBeShown,
+         "sidebar open pref should be " + shouldBeShown);
+    if (shouldBeShown) {
       is(browser.getAttribute('src'), Social.provider.sidebarURL, "sidebar url should be set");
+      // We don't currently check docShellIsActive as this is only set
+      // after load event fires, and the tests below explicitly wait for this
+      // anyway.
+    }
+    else {
+      ok(!browser.docShellIsActive, "sidebar should have an inactive docshell");
+      // sidebar will only be immediately unloaded (and thus set to
+      // about:blank) when canShow is false.
+      if (SocialSidebar.canShow) {
+        // should not have unloaded so will still be the provider URL.
+        is(browser.getAttribute('src'), Social.provider.sidebarURL, "sidebar url should be set");
+      } else {
+        // should have been an immediate unload.
+        is(browser.getAttribute('src'), "about:blank", "sidebar url should be blank");
+      }
+    }
   }
 
   // First check the the sidebar is initially visible, and loaded
   ok(!command.hidden, "toggle command should be visible");
   checkShown(true);
 
   browser.addEventListener("socialFrameHide", function sidebarhide() {
     browser.removeEventListener("socialFrameHide", sidebarhide);
 
     checkShown(false);
 
     browser.addEventListener("socialFrameShow", function sidebarshow() {
       browser.removeEventListener("socialFrameShow", sidebarshow);
 
       checkShown(true);
 
+      // Set Social.enabled = false and check everything is as expected.
+      Social.enabled = false;
+      checkShown(false);
+
+      Social.enabled = true;
+      checkShown(true);
+
+      // And an edge-case - disable social and reset the provider.
+      Social.provider = null;
+      Social.enabled = false;
+      checkShown(false);
+
       // Finish the test
       finishcb();
     });
 
     // Toggle it back on
     info("Toggling sidebar back on");
     Social.toggleSidebar();
   });
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -28,17 +28,17 @@
              because of the downloads summary at the bottom of the list of
              download items. An element in the summary has the same min-width
              on a description, and we don't want the panel to change size if the
              summary isn't being displayed, so we ensure that items share the
              same minimum width.
              -->
         <xul:description class="downloadTarget"
                          crop="center"
-                         style="min-width: &downloadsSummary.minWidth;"
+                         style="min-width: &downloadsSummary.minWidth2;"
                          xbl:inherits="value=target,tooltiptext=target"/>
         <xul:progressmeter anonid="progressmeter"
                            class="downloadProgress"
                            min="0"
                            max="100"
                            xbl:inherits="mode=progressmode,value=progress"/>
         <xul:description class="downloadDetails"
                          style="width: &downloadDetails.width;"
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -111,17 +111,17 @@
               align="center"
               orient="horizontal"
               onkeypress="DownloadsSummary.onKeyPress(event);"
               onclick="DownloadsSummary.onClick(event);">
           <image class="downloadTypeIcon" />
           <vbox>
             <description id="downloadsSummaryDescription"
                          class="downloadTarget"
-                         style="min-width: &downloadsSummary.minWidth;"/>
+                         style="min-width: &downloadsSummary.minWidth2;"/>
             <progressmeter id="downloadsSummaryProgress"
                            class="downloadProgress"
                            min="0"
                            max="100"
                            mode="normal" />
             <description id="downloadsSummaryDetails"
                          class="downloadDetails"
                          style="width: &downloadDetails.width;"
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -70,17 +70,17 @@ const kDownloadsStringsRequiringFormatti
   shortTimeLeftHours: true,
   shortTimeLeftDays: true,
   statusSeparator: true,
   statusSeparatorBeforeNumber: true,
   fileExecutableSecurityWarning: true
 };
 
 const kDownloadsStringsRequiringPluralForm = {
-  otherDownloads: true
+  otherDownloads2: true
 };
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLocalFileCtor", function () {
   return Components.Constructor("@mozilla.org/file/local;1",
                                 "nsILocalFile", "initWithPath");
 });
 
 const kPartialDownloadSuffix = ".part";
@@ -1855,17 +1855,17 @@ DownloadsSummaryData.prototype = {
    */
   _refreshProperties: function DSD_refreshProperties()
   {
     // Pre-load summary with default values.
     let summary =
       DownloadsCommon.summarizeDownloads(this._dataItemsForSummary());
 
     this._description = DownloadsCommon.strings
-                                       .otherDownloads(summary.numActive);
+                                       .otherDownloads2(summary.numActive);
     this._percentComplete = summary.percentComplete;
 
     // If all downloads are paused, show the progress indicator as paused.
     this._showingProgress = summary.numDownloading > 0 ||
                             summary.numPaused > 0;
 
     // Display the estimated time left, if present.
     if (summary.rawTimeLeft == -1) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -276,16 +276,26 @@ BrowserGlue.prototype = {
         }
         break;
       case "initial-migration-will-import-default-bookmarks":
         this._migrationImportsDefaultBookmarks = true;
         break;
       case "initial-migration-did-import-default-bookmarks":
         this._initPlaces(true);
         break;
+      case "handle-xul-text-link":
+        let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
+        if (!linkHandled.data) {
+          let win = this.getMostRecentBrowserWindow();
+          if (win) {
+            win.openUILinkIn(data, "tab");
+            linkHandled.data = true;
+          }
+        }
+        break;
     }
   }, 
 
   // initialization (called on application startup) 
   _init: function BG__init() {
     let os = Services.obs;
     os.addObserver(this, "xpcom-shutdown", false);
     os.addObserver(this, "prefservice:after-app-defaults", false);
@@ -307,16 +317,17 @@ BrowserGlue.prototype = {
     os.addObserver(this, "places-init-complete", false);
     this._isPlacesInitObserver = true;
     os.addObserver(this, "places-database-locked", false);
     this._isPlacesLockedObserver = true;
     os.addObserver(this, "distribution-customization-complete", false);
     os.addObserver(this, "places-shutdown", false);
     this._isPlacesShutdownObserver = true;
     os.addObserver(this, "defaultURIFixup-using-keyword-pref", false);
+    os.addObserver(this, "handle-xul-text-link", false);
   },
 
   // cleanup (called on application shutdown)
   _dispose: function BG__dispose() {
     let os = Services.obs;
     os.removeObserver(this, "xpcom-shutdown");
     os.removeObserver(this, "prefservice:after-app-defaults");
     os.removeObserver(this, "final-ui-startup");
@@ -337,16 +348,17 @@ BrowserGlue.prototype = {
       this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
     if (this._isPlacesInitObserver)
       os.removeObserver(this, "places-init-complete");
     if (this._isPlacesLockedObserver)
       os.removeObserver(this, "places-database-locked");
     if (this._isPlacesShutdownObserver)
       os.removeObserver(this, "places-shutdown");
     os.removeObserver(this, "defaultURIFixup-using-keyword-pref");
+    os.removeObserver(this, "handle-xul-text-link");
     UserAgentOverrides.uninit();
     webappsUI.uninit();
     SignInToWebsiteUX.uninit();
     webrtcUI.uninit();
   },
 
   _onAppDefaults: function BG__onAppDefaults() {
     // apply distribution customizations (prefs)
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -434,16 +434,21 @@ PrivateBrowsingService.prototype = {
             this.privateBrowsingEnabled = true;
         }
         this._obs.removeObserver(this, "profile-after-change");
         break;
       case "quit-application-granted":
         this._unload();
         break;
       case "private-browsing":
+        // clear all auth tokens
+        let sdr = Cc["@mozilla.org/security/sdr;1"].
+                  getService(Ci.nsISecretDecoderRing);
+        sdr.logoutAndTeardown();
+    
         if (!this._inPrivateBrowsing) {
           // Clear the error console
           let consoleService = Cc["@mozilla.org/consoleservice;1"].
                                getService(Ci.nsIConsoleService);
           consoleService.logStringMessage(null); // trigger the listeners
           consoleService.reset();
         }
         break;
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_ui.js
+++ b/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_ui.js
@@ -35,17 +35,17 @@ function test() {
             aCallback();
             Services.obs.removeObserver(observer2, "domwindowclosed");
           }, "domwindowclosed", false);
           aSubject.close();
         });
         Services.obs.removeObserver(observer1, "domwindowopened");
       }, false);
     }, "domwindowopened", false);
-    OpenBrowserWindow();
+    OpenBrowserWindow({private: PrivateBrowsingUtils.isWindowPrivate(window)});
   }
 
   // test the gPrivateBrowsingUI object
   ok(gPrivateBrowsingUI, "The gPrivateBrowsingUI object exists");
   is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started initially");
   is(gPrivateBrowsingUI.privateBrowsingEnabled, false, "gPrivateBrowsingUI should expose the correct private browsing status");
   is(PrivateBrowsingUtils.isWindowPrivate(window), false, "PrivateBrowsingUtils should expose the correct per-window private browsing status");
   ok(pbMenuItem, "The Private Browsing menu item exists");
--- a/browser/components/privatebrowsing/test/browser/obsolete/browser_privatebrowsing_newwindow_stopcmd.js
+++ b/browser/components/privatebrowsing/test/browser/obsolete/browser_privatebrowsing_newwindow_stopcmd.js
@@ -8,17 +8,17 @@
 function test() {
   // initialization
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
   waitForExplicitFinish();
 
   pb.privateBrowsingEnabled = true;
 
-  let win = OpenBrowserWindow();
+  let win = OpenBrowserWindow({private: PrivateBrowsingUtils.isWindowPrivate(window)});
   win.addEventListener("load", function() {
     win.removeEventListener("load", arguments.callee, false);
     executeSoon(function() {
       let cmd = win.document.getElementById("Tools:PrivateBrowsing");
       ok(!cmd.hasAttribute("disabled"),
          "The Private Browsing command in a new window should be enabled");
 
       win.close();
--- a/browser/components/shell/src/nsGNOMEShellService.cpp
+++ b/browser/components/shell/src/nsGNOMEShellService.cpp
@@ -25,16 +25,17 @@
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIImageLoadingContent.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "prprf.h"
 #ifdef MOZ_WIDGET_GTK2
 #include "nsIImageToPixbuf.h"
 #endif
+#include "nsXULAppAPI.h"
 
 #include <glib.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <limits.h>
 #include <stdlib.h>
@@ -103,23 +104,20 @@ nsGNOMEShellService::Init()
   if (GetAppPathFromLauncher())
     return NS_OK;
 
   nsCOMPtr<nsIProperties> dirSvc
     (do_GetService("@mozilla.org/file/directory_service;1"));
   NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE);
 
   nsCOMPtr<nsIFile> appPath;
-  rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
+  rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
                    getter_AddRefs(appPath));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = appPath->AppendNative(NS_LITERAL_CSTRING(MOZ_APP_NAME));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   return appPath->GetNativePath(mAppPath);
 }
 
 NS_IMPL_ISUPPORTS1(nsGNOMEShellService, nsIShellService)
 
 bool
 nsGNOMEShellService::GetAppPathFromLauncher()
 {
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -21,16 +21,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIWindowsRegKey.h"
 #include "nsUnicharUtils.h"
 #include "nsIWinTaskbar.h"
 #include "nsISupportsPrimitives.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 #include "windows.h"
 #include "shellapi.h"
 
 #ifdef _WIN32_WINNT
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0600
@@ -193,22 +194,22 @@ nsresult
 GetHelperPath(nsAutoString& aPath)
 {
   nsresult rv;
   nsCOMPtr<nsIProperties> directoryService = 
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIFile> appHelper;
-  rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+  rv = directoryService->Get(XRE_EXECUTABLE_FILE,
                              NS_GET_IID(nsIFile),
                              getter_AddRefs(appHelper));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall"));
+  rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = appHelper->GetPath(aPath);
 
   aPath.Insert(L'"', 0);
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -18,41 +18,58 @@ if test "$OS_ARCH" = "WINNT"; then
             "$MOZ_UPDATE_CHANNEL" = "release"; then
       if ! test "$MOZ_DEBUG"; then
         MOZ_STUB_INSTALLER=1
       fi
     fi
   fi
 fi
 
+# The value of ACCEPTED_MAR_CHANNEL_IDS should usually be the same as the value
+# MAR_CHANNEL_ID. If more than one ID is needed, then you should use a comma
+# separated list of values.
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+case "$MOZ_UPDATE_CHANNEL" in
+release|aurora|esr)
+  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-$MOZ_UPDATE_CHANNEL
+  MAR_CHANNEL_ID=firefox-mozilla-$MOZ_UPDATE_CHANNEL
+  ;;
+beta)
+  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-beta,firefox-mozilla-release
+  MAR_CHANNEL_ID=firefox-mozilla-beta
+  ;;
+*)
+  ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
+  MAR_CHANNEL_ID=firefox-mozilla-central
+  ;;
+esac
+
+# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
+# Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
+# because branding dependencies are broken.
+# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
+# specified. It should never point to the "official" branding directory.
+# For mozilla-beta, mozilla-release, or mozilla-central repositories, use
+# "nightly" branding (until bug 659568 is fixed).
+# For the mozilla-aurora repository, use "aurora".
+MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
+if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then
+  MOZ_BRANDING_DIRECTORY=browser/branding/aurora
+else
+  MOZ_BRANDING_DIRECTORY=browser/branding/nightly
+fi
+
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_AITC=1
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_CRYPTO=1
 MOZ_SERVICES_METRICS=1
 MOZ_SERVICES_NOTIFICATIONS=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gio"
-# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
-# Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
-# because branding dependencies are broken.
-# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
-# specified. It should never point to the "official" branding directory.
-# For mozilla-beta, mozilla-release, or mozilla-central repositories, use
-# "nightly" branding (until bug 659568 is fixed).
-# For the mozilla-aurora repository, use "aurora".
-MOZ_BRANDING_DIRECTORY=browser/branding/nightly
-MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
 MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
-# This should usually be the same as the value MAR_CHANNEL_ID.
-# If more than one ID is needed, then you should use a comma separated list
-# of values.
-ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
-# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
-MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBAPP_RUNTIME=1
 MOZ_MEDIA_NAVIGATOR=1
-MOZ_PER_WINDOW_PRIVATE_BROWSING=1
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -531,19 +531,17 @@ ChromeDebuggerProcess.prototype = {
   },
 
   /**
    * Creates and initializes the profile & process for the remote debugger.
    */
   _create: function RDP__create() {
     this.globalUI._chromeDebugger = this;
 
-    let file = FileUtils.getFile("CurProcD",
-      [Services.appinfo.OS == "WINNT" ? "firefox.exe"
-                                      : "firefox-bin"]);
+    let file = Services.dirsvc.get("XREExeF", Ci.nsIFile);
 
     let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
     process.init(file);
 
     let args = [
       "-no-remote", "-P", this._dbgProfile.name,
       "-chrome", DBG_XUL];
 
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -6,17 +6,17 @@ DEPTH           = @DEPTH@
 topsrcdir       = @top_srcdir@
 srcdir          = @srcdir@
 VPATH           = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_TESTS = \
-	browser_dbg_leaktest.js \
+	browser_dbg_aaa_run_first_leaktest.js \
 	browser_dbg_createChrome.js \
 	$(browser_dbg_debugger-tab-switch.js disabled until issues 106, 40 are fixed) \
 	$(browser_dbg_debugger-tab-switch-window.js disabled until issues 106, 40 are fixed) \
 	browser_dbg_debuggerstatement.js \
 	browser_dbg_listtabs.js \
 	browser_dbg_tabactor-01.js \
 	browser_dbg_tabactor-02.js \
 	browser_dbg_globalactor-01.js \
rename from browser/devtools/debugger/test/browser_dbg_leaktest.js
rename to browser/devtools/debugger/test/browser_dbg_aaa_run_first_leaktest.js
--- a/browser/devtools/debugger/test/browser_dbg_leaktest.js
+++ b/browser/devtools/debugger/test/browser_dbg_aaa_run_first_leaktest.js
@@ -15,16 +15,20 @@ let gDebugger = null;
 
 function test()
 {
   let scriptShown = false;
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
+  // Wait longer for this very simple test that comes first, to make sure that
+  // GC from previous tests does not interfere with the debugger suite.
+  requestLongerTimeout(2);
+
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.panelWin;
     resumed = true;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -20,30 +20,30 @@
      For example, in English, a long string would be:
 
      59 minutes, 59 seconds remaining - 1022 of 1023 KB
 
      That's 50 characters, so we set the width at 50ch.
      -->
 <!ENTITY downloadDetails.width            "50ch">
 
-<!-- LOCALIZATION NOTE (downloadsSummary.minWidth):
+<!-- LOCALIZATION NOTE (downloadsSummary.minWidth2):
      Minimum width for the main description of the downloads summary,
      which is displayed at the bottom of the Downloads Panel if the
      number of downloads exceeds the limit that the panel can display.
 
-     A good rule of thumb here is to look at the otherDownloads string
+     A good rule of thumb here is to look at the otherDownloads2 string
      in downloads.properties, and make a reasonable estimate of its
      maximum length. For English, this seems like a reasonable limit:
 
-     +999 other current downloads
+     + 999 other downloads
 
-     that's 28 characters, so we set the minimum width to 28ch.
+     that's 21 characters, so we set the minimum width to 21ch.
      -->
-<!ENTITY downloadsSummary.minWidth        "28ch">
+<!ENTITY downloadsSummary.minWidth2       "21ch">
 
 <!ENTITY cmd.pause.label                  "Pause">
 <!ENTITY cmd.pause.accesskey              "P">
 <!ENTITY cmd.resume.label                 "Resume">
 <!ENTITY cmd.resume.accesskey             "R">
 <!ENTITY cmd.cancel.label                 "Cancel">
 <!ENTITY cmd.cancel.accesskey             "C">
 <!-- LOCALIZATION NOTE (cmd.show.label, cmd.show.accesskey, cmd.showMac.label,
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.properties
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.properties
@@ -66,14 +66,14 @@ shortTimeLeftDays=%1$Sd
 # even for right-to-left languages, unless the defaults are not suitable.
 statusSeparator=%1$S \u2014 %2$S
 statusSeparatorBeforeNumber=%1$S \u2014  %2$S
 
 fileExecutableSecurityWarning="%S" is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch "%S"?
 fileExecutableSecurityWarningTitle=Open Executable File?
 fileExecutableSecurityWarningDontAsk=Don't ask me this again
 
-# LOCALIZATION NOTE (otherDownloads):
+# LOCALIZATION NOTE (otherDownloads2):
 # This is displayed in an item at the bottom of the Downloads Panel when
 # there are more downloads than can fit in the list in the panel. Use a
 # semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
-otherDownloads=+%1$S other current download; +%1$S other current downloads
+otherDownloads2=+ %1$S other download; + %1$S other downloads
--- a/browser/modules/test/browser_TelemetryTimestamps.js
+++ b/browser/modules/test/browser_TelemetryTimestamps.js
@@ -4,16 +4,25 @@
 function getSimpleMeasurementsFromTelemetryPing() {
   const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
   let ping = TelemetryPing.getPayload();
 
   return ping.simpleMeasurements;
 }
 
 function test() {
+  waitForExplicitFinish()
+  const Telemetry = Services.telemetry;
+  Telemetry.asyncReadShutdownTime(function () {
+    actualTest();
+    finish();
+  });
+}
+
+function actualTest() {
   // Test the module logic
   let tmp = {};
   Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
   let TelemetryTimestamps = tmp.TelemetryTimestamps;
   let now = Date.now();
   TelemetryTimestamps.add("foo");
   ok(TelemetryTimestamps.get().foo, "foo was added");
   ok(TelemetryTimestamps.get().foo >= now, "foo has a reasonable value");
--- a/config/makefiles/xpcshell.mk
+++ b/config/makefiles/xpcshell.mk
@@ -10,17 +10,17 @@ ifndef INCLUDED_TESTS_XPCSHELL_MK #{
 
 ifdef XPCSHELL_TESTS #{
 
 ifndef relativesrcdir
 $(error Must define relativesrcdir when defining XPCSHELL_TESTS.)
 endif
 
 define _INSTALL_TESTS
-$(call install_cmd, $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
+$(call install_cmd, $(filter-out %~,$(wildcard $(srcdir)/$(dir)/*)) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 endef # do not remove the blank line!
 
 SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
 
 testxpcsrcdir = $(topsrcdir)/testing/xpcshell
 
 libs:: libs-xpcshell-tests
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -74,18 +74,18 @@ class ImageLoader;
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xcc604bdc, 0xd55e, 0x4918, \
- { 0xaa, 0x82, 0xb2, 0xde, 0xbf, 0x01, 0x09, 0x5d } }
+{ 0xcb362f1b, 0x8a05, 0x4d4f, \
+  { 0x90, 0x63, 0xf2, 0x5f, 0x8b, 0x8c, 0xb2, 0xe1 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -395,16 +395,32 @@ public:
    * change to actually change anything immediately.
    * @see nsBidiUtils.h
    */
   void SetBidiOptions(uint32_t aBidiOptions)
   {
     mBidiOptions = aBidiOptions;
   }
 
+  /**
+   * Get the has mixed active content loaded flag for this document.
+   */
+  bool GetHasMixedActiveContentLoaded()
+  {
+    return mHasMixedActiveContentLoaded;
+  }
+
+  /**
+   * Set the has mixed active content loaded flag for this document.
+   */
+  void SetHasMixedActiveContentLoaded(bool aHasMixedActiveContentLoaded)
+  {
+    mHasMixedActiveContentLoaded = aHasMixedActiveContentLoaded;
+  }
+
 
   /**
    * Get the sandbox flags for this document.
    * @see nsSandboxFlags.h for the possible flags
    */
   uint32_t GetSandboxFlags() const
   {
     return mSandboxFlags;
@@ -1894,16 +1910,19 @@ protected:
   bool mNeedLayoutFlush;
 
   // True if a style flush might not be a no-op
   bool mNeedStyleFlush;
 
   // True if a DOMMutationObserver is perhaps attached to a node in the document.
   bool mMayHaveDOMMutationObservers;
 
+  // True if a document has loaded Mixed Active Script (see nsMixedContentBlocker.cpp)
+  bool mHasMixedActiveContentLoaded;
+
   // The document's script global object, the object from which the
   // document can get its script context and scope. This is the
   // *inner* window object.
   nsCOMPtr<nsIScriptGlobalObject> mScriptGlobalObject;
 
   // If mIsStaticDocument is true, mOriginalDocument points to the original
   // document.
   nsCOMPtr<nsIDocument> mOriginalDocument;
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -1,32 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIRequest;
 interface nsIFrame;
 interface nsIObjectFrame;
 interface nsIPluginTag;
 interface nsIDOMElement;
 interface nsIDOMClientRect;
 interface nsIURI;
 
 %{C++
 #include "nsNPAPIPluginInstance.h"
 %}
 [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
 
 /**
  * This interface represents a content node that loads objects.
  */
 
-[scriptable, uuid(649b8a75-8623-437f-ad30-0ac596c8452e)]
+[scriptable, uuid(e9102696-4bb4-4a98-a31f-be9da5a3d18e)]
 interface nsIObjectLoadingContent : nsISupports
 {
   /**
    * See notes in nsObjectLoadingContent.h
    */
   const unsigned long TYPE_LOADING  = 0;
   const unsigned long TYPE_IMAGE    = 1;
   const unsigned long TYPE_PLUGIN   = 2;
@@ -126,16 +127,29 @@ interface nsIObjectLoadingContent : nsIS
   readonly attribute boolean activated;
 
   [noscript] void stopPluginInstance();
 
   [noscript] void syncStartPluginInstance();
   [noscript] void asyncStartPluginInstance();
 
   /**
+   * Puts the tag in the "waiting on a channel" state and adopts this
+   * channel. This does not override the normal logic of examining attributes
+   * and the channel type, so the load may cancel this channel if it decides not
+   * to use one.
+   *
+   * This assumes:
+   *  - This tag has not begun loading yet
+   *  - This channel has not yet hit OnStartRequest
+   *  - The caller will continue to pass channel events to us as a listener
+   */
+  [noscript] void initializeFromChannel(in nsIRequest request);
+
+  /**
    * Requests the plugin instance for scripting, attempting to spawn it if
    * appropriate.
    *
    * The first time content js tries to access a pre-empted plugin
    * (click-to-play or play preview), an event is dispatched.
    */
   [noscript] nsNPAPIPluginInstancePtr
     scriptRequestPluginInstance(in bool callerIsContentJS);
--- a/content/base/src/nsMixedContentBlocker.cpp
+++ b/content/base/src/nsMixedContentBlocker.cpp
@@ -4,65 +4,106 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsMixedContentBlocker.h"
 #include "nsContentPolicyUtils.h"
 
 #include "nsINode.h"
 #include "nsCOMPtr.h"
 #include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
 #include "nsISecurityEventSink.h"
 #include "nsIWebProgressListener.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
+#include "nsIRequest.h"
+#include "nsIDocument.h"
+#include "nsIContentViewer.h"
+#include "nsIChannel.h"
+#include "nsIHttpChannel.h"
 #include "mozilla/Preferences.h"
+#include "nsIScriptObjectPrincipal.h"
+
+#include "prlog.h"
 
 using namespace mozilla;
 
 // Is mixed script blocking (fonts, plugin content, scripts, stylesheets,
 // iframes, websockets, XHR) enabled?
 bool nsMixedContentBlocker::sBlockMixedScript = false;
 
 // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
 bool nsMixedContentBlocker::sBlockMixedDisplay = false;
 
 // Fired at the document that attempted to load mixed content.  The UI could
 // handle this event, for example, by displaying an info bar that offers the
 // choice to reload the page with mixed content permitted.
-//
-// Disabled for now until bug 782654 is fixed
-/*
-class nsMixedContentBlockedEvent : public nsRunnable
+class nsMixedContentEvent : public nsRunnable
 {
 public:
-  nsMixedContentBlockedEvent(nsISupports *aContext, unsigned short aType)
+  nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType)
     : mContext(aContext), mType(aType)
   {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(mContext,
                  "You can't call this runnable without a requesting context");
 
     // To update the security UI in the tab with the blocked mixed content, call
     // nsISecurityEventSink::OnSecurityChange.  You can get to the event sink by
     // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to
     // nsISecurityEventSink.
 
+
+    // Mixed content was allowed and is about to load; get the document and
+    // set the approriate flag to true if we are about to load Mixed Active
+    // Content.
+    nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext);
+    nsCOMPtr<nsIDocShellTreeItem> currentDocShellTreeItem(do_QueryInterface(docShell));
+    if (!currentDocShellTreeItem) {
+        return NS_OK;
+    }
+    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+    currentDocShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+    NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
+
+    // now get the document from sameTypeRoot
+    nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
+    NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
+
+
+    if (mType == eMixedScript) {
+      rootDoc->SetHasMixedActiveContentLoaded(true);
+
+      // Update the security UI in the tab with the blocked mixed content
+      nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
+      if (eventSink) {
+        eventSink->OnSecurityChange(mContext, nsIWebProgressListener::STATE_IS_BROKEN);
+      }
+
+    } else {
+        if (mType == eMixedDisplay) {
+          //Do Nothing for now; state will already be set STATE_IS_BROKEN
+        }
+    }
+
+
+
     return NS_OK;
   }
 private:
   // The requesting context for the content load. Generally, a DOM node from
   // the document that caused the load.
   nsCOMPtr<nsISupports> mContext;
 
-  // The type of mixed content that was blocked, e.g. active or display
-  unsigned short mType;
+  // The type of mixed content detected, e.g. active or display
+  const MixedContentTypes mType;
 };
-*/
+
 
 nsMixedContentBlocker::nsMixedContentBlocker()
 {
   // Cache the pref for mixed script blocking
   Preferences::AddBoolVarCache(&sBlockMixedScript,
                                "security.mixed_content.block_active_content");
 
   // Cache the pref for mixed display blocking
@@ -81,54 +122,122 @@ nsMixedContentBlocker::ShouldLoad(uint32
                                   nsIURI* aContentLocation,
                                   nsIURI* aRequestingLocation,
                                   nsISupports* aRequestingContext,
                                   const nsACString& aMimeGuess,
                                   nsISupports* aExtra,
                                   nsIPrincipal* aRequestPrincipal,
                                   int16_t* aDecision)
 {
-  // Default policy: allow the load if we find no reason to block it.
-  *aDecision = nsIContentPolicy::ACCEPT;
+  // Asserting that we are on the main thread here and hence do not have to lock
+  // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing
+  // to them.
+  MOZ_ASSERT(NS_IsMainThread());
 
-  // If mixed script blocking and mixed display blocking are turned off
-  // we can return early
-  if (!sBlockMixedScript && !sBlockMixedDisplay) {
-    return NS_OK;
-  }
+  // Assume active (high risk) content and blocked by default
+  MixedContentTypes classification = eMixedScript;
 
-  // Top-level load cannot be mixed content so allow it.
-  // Creating insecure websocket connections in a secure page is blocked already in websocket constructor.
-  if (aContentType == nsIContentPolicy::TYPE_DOCUMENT || aContentType == nsIContentPolicy::TYPE_WEBSOCKET) {
-    return NS_OK;
-  }
 
-  // We need aRequestingLocation to pull out the scheme. If it isn't passed
-  // in, get it from the DOM node.
-  if (!aRequestingLocation) {
-    nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext);
-    if (node) {
-      nsCOMPtr<nsIURI> principalUri;
-      node->NodePrincipal()->GetURI(getter_AddRefs(principalUri));
-      aRequestingLocation = principalUri;
-    }
-    // If we still don't have a requesting location then we can't tell if
-    // this is a mixed content load.  Deny to be safe.
-    if (!aRequestingLocation) {
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+  // Notes on non-obvious decisions:
+  //
+  // TYPE_DTD: A DTD can contain entity definitions that expand to scripts.
+  //
+  // TYPE_FONT: The TrueType hinting mechanism is basically a scripting
+  // language that gets interpreted by the operating system's font rasterizer.
+  // Mixed content web fonts are relatively uncommon, and we can can fall back
+  // to built-in fonts with minimal disruption in almost all cases.
+  //
+  // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a
+  // script that a plugin will execute) or display content (e.g. Flash video
+  // content).
+  //
+  // TYPE_CSP_REPORT: High-risk because they directly leak information about
+  // the content of the page, and because blocking them does not have any
+  // negative effect on the page loading.
+  //
+  // TYPE_PING: Ping requests are POSTS, not GETs like images and media.
+  // Also, PING requests have no bearing on the rendering or operation of
+  // the page when used as designed, so even though they are lower risk than
+  // scripts, blocking them is basically risk-free as far as compatibility is
+  // concerned.  Ping is turned off by default in Firefox, so unless a user
+  // opts into ping, no request will be made.  Categorizing this as Mixed
+  // Display Content for now, but this is subject to change.
+  //
+  // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning
+  // and other advanced CSS features can possibly be exploited to cause
+  // spoofing attacks (e.g. make a "grant permission" button look like a
+  // "refuse permission" button).
+  //
+  // TYPE_WEBSOCKET: The Websockets API requires browsers to
+  // reject mixed-content websockets: "If secure is false but the origin of
+  // the entry script has a scheme component that is itself a secure protocol,
+  // e.g. HTTPS, then throw a SecurityError exception." We already block mixed
+  // content websockets within the websockets implementation, so we don't need
+  // to do any blocking here, nor do we need to provide a way to undo or
+  // override the blocking. Websockets without TLS are very flaky anyway in the
+  // face of many HTTP-aware proxies. Compared to psasive content, there is
+  // additional risk that the script using WebSockets will disclose sensitive
+  // information from the HTTPS page and/or eval (directly or indirectly)
+  // received data.
+  //
+  // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most
+  // mixed-content XHR will already be blocked by that check. This will also
+  // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned
+  // above for WebSockets apply to XHR, and XHR should have the same security
+  // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects
+  // amplifies these concerns.
+
+
+  MOZ_STATIC_ASSERT(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST,
+                    "TYPE_DATAREQUEST is not a synonym for "
+                    "TYPE_XMLHTTPREQUEST");
+
+  switch (aContentType) {
+    // The top-level document cannot be mixed content by definition
+    case TYPE_DOCUMENT:
+      *aDecision = ACCEPT;
       return NS_OK;
-    }
-  }
+    // Creating insecure websocket connections in a secure page is blocked already
+    // in the websocket constructor. We don't need to check the blocking here
+    // and we don't want to un-block
+    case TYPE_WEBSOCKET:
+      *aDecision = ACCEPT;
+      return NS_OK;
+
+
+    // Static display content is considered moderate risk for mixed content so
+    // these will be blocked according to the mixed display preference
+    case TYPE_IMAGE:
+    case TYPE_MEDIA:
+    case TYPE_PING:
+      classification = eMixedDisplay;
+      break;
 
-  // Check the parent scheme. If it is not an HTTPS page then mixed content
-  // restrictions do not apply.
-  bool parentIsHttps;
-  if (NS_FAILED(aRequestingLocation->SchemeIs("https", &parentIsHttps)) ||
-      !parentIsHttps) {
-    return NS_OK;
+    // Active content (or content with a low value/risk-of-blocking ratio)
+    // that has been explicitly evaluated; listed here for documentation
+    // purposes and to avoid the assertion and warning for the default case.
+    case TYPE_CSP_REPORT:
+    case TYPE_DTD:
+    case TYPE_FONT:
+    case TYPE_OBJECT:
+    case TYPE_OBJECT_SUBREQUEST:
+    case TYPE_SCRIPT:
+    case TYPE_STYLESHEET:
+    case TYPE_SUBDOCUMENT:
+    case TYPE_XBL:
+    case TYPE_XMLHTTPREQUEST:
+    case TYPE_OTHER:
+      break;
+
+
+    // This content policy works as a whitelist.
+    default:
+      MOZ_NOT_REACHED("Mixed content of unknown type");
+      NS_WARNING("Mixed content of unknown type");
+      break;
   }
 
  /* Get the scheme of the sub-document resource to be requested. If it is
   * a safe to load in an https context then mixed content doesn't apply.
   *
   * Check Protocol Flags to determine if scheme is safe to load:
   * URI_DOES_NOT_RETURN_DATA - e.g.
   *   "mailto"
@@ -153,67 +262,89 @@ nsMixedContentBlocker::ShouldLoad(uint32
       NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) {
     return NS_ERROR_FAILURE;
   }
 
   if (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure) {
      return NS_OK;
   }
 
-  // If we are here we have mixed content.
-
-  // Decide whether or not to allow the mixed content based on what type of
-  // content it is and if the user permitted it.
-  switch (aContentType) {
-    case nsIContentPolicy::TYPE_FONT:
-    case nsIContentPolicy::TYPE_OBJECT:
-    case nsIContentPolicy::TYPE_SCRIPT:
-    case nsIContentPolicy::TYPE_STYLESHEET:
-    case nsIContentPolicy::TYPE_SUBDOCUMENT:
-    case nsIContentPolicy::TYPE_WEBSOCKET:
-    case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
-      // fonts, plugin content, scripts, stylesheets, iframes, websockets and
-      // XHRs are considered high risk for mixed content so these are blocked
-      // per the mixed script preference
-      if (sBlockMixedScript) {
-        *aDecision = nsIContentPolicy::REJECT_REQUEST;
+  // We need aRequestingLocation to pull out the scheme. If it isn't passed
+  // in, get it from the aRequestingPricipal
+  if (!aRequestingLocation) {
+    if (!aRequestPrincipal) {
+      // If we don't have aRequestPrincipal, try getting it from the
+      // DOM node using aRequestingContext
+      nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext);
+      if (node) {
+        aRequestPrincipal = node->NodePrincipal();
+      } else {
+        // Try using the window's script object principal if it's not a node.
+        nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aRequestingContext);
+        if (scriptObjPrin) {
+          aRequestPrincipal = scriptObjPrin->GetPrincipal();
+        }
+      }
+    }
+    if (aRequestPrincipal) {
+      nsCOMPtr<nsIURI> principalUri;
+      nsresult rvalue = aRequestPrincipal->GetURI(getter_AddRefs(principalUri));
+      if (NS_SUCCEEDED(rvalue)) {
+        aRequestingLocation = principalUri;
+      }
+    }
 
-        // Fire the event from a script runner as it is unsafe to run script
-        // from within ShouldLoad
-        // Disabled until bug 782654 is fixed.
-        /*
-        nsContentUtils::AddScriptRunner(
-          new nsMixedContentBlockedEvent(aRequestingContext, eBlockedMixedScript));
-        */
+    if (!aRequestingLocation) {
+      // If content scripts from an addon are causing this load, they have an
+      // ExpandedPrincipal instead of a Principal. This is pseudo-privileged code, so allow
+      // the load. Or if this is system principal, allow the load.
+      nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aRequestPrincipal);
+      if (expanded || (aRequestPrincipal && nsContentUtils::IsSystemPrincipal(aRequestPrincipal))) {
+        *aDecision = ACCEPT;
+        return NS_OK;
+      } else {
+        // We still don't have a requesting location and there is no Expanded Principal.
+        // We can't tell if this is a mixed content load.  Deny to be safe.
+        *aDecision = REJECT_REQUEST;
+        return NS_OK;
       }
-      break;
-
-    case nsIContentPolicy::TYPE_IMAGE:
-    case nsIContentPolicy::TYPE_MEDIA:
-    case nsIContentPolicy::TYPE_PING:
-      // display (static) content are considered moderate risk for mixed content
-      // so these will be blocked according to the mixed display preference
-      if (sBlockMixedDisplay) {
-        *aDecision = nsIContentPolicy::REJECT_REQUEST;
-
-        // Fire the event from a script runner as it is unsafe to run script
-        // from within ShouldLoad
-        // Disabled until bug 782654 is fixed.
-        /*
-        nsContentUtils::AddScriptRunner(
-          new nsMixedContentBlockedEvent(aRequestingContext, eBlockedMixedDisplay));
-        */
-      }
-      break;
-
-    default:
-      // other types of mixed content are allowed
-      break;
+    }
   }
 
+  // Check the parent scheme. If it is not an HTTPS page then mixed content
+  // restrictions do not apply.
+  bool parentIsHttps;
+  nsresult rv = aRequestingLocation->SchemeIs("https", &parentIsHttps);
+  if (NS_FAILED(rv)) {
+    NS_ERROR("aRequestingLocation->SchemeIs failed");
+    *aDecision = REJECT_REQUEST;
+    return NS_OK;
+  }
+  if (!parentIsHttps) {
+    *aDecision = ACCEPT;
+    return NS_OK;
+  }
+
+  // If we are here we have mixed content.
+  // If the content is display content, and the pref says display content should be blocked, block it.
+  // If the content is mixed content, and the pref says mixed content should be blocked, block it.
+  if ( (sBlockMixedDisplay && classification == eMixedDisplay) || (sBlockMixedScript && classification == eMixedScript) ) {
+     *aDecision = nsIContentPolicy::REJECT_REQUEST;
+     return NS_OK;
+  } else {
+    // The content is not blocked by the mixed content prefs.
+
+    // Fire the event from a script runner as it is unsafe to run script
+    // from within ShouldLoad
+    nsContentUtils::AddScriptRunner(
+      new nsMixedContentEvent(aRequestingContext, classification));
+    return NS_OK;
+  }
+
+  *aDecision = REJECT_REQUEST;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
                                      nsIURI* aContentLocation,
                                      nsIURI* aRequestingLocation,
                                      nsISupports* aRequestingContext,
--- a/content/base/src/nsMixedContentBlocker.h
+++ b/content/base/src/nsMixedContentBlocker.h
@@ -7,24 +7,24 @@
 #define nsMixedContentBlocker_h___
 
 #define NS_MIXEDCONTENTBLOCKER_CONTRACTID "@mozilla.org/mixedcontentblocker;1"
 /* daf1461b-bf29-4f88-8d0e-4bcdf332c862 */
 #define NS_MIXEDCONTENTBLOCKER_CID \
 { 0xdaf1461b, 0xbf29, 0x4f88, \
   { 0x8d, 0x0e, 0x4b, 0xcd, 0xf3, 0x32, 0xc8, 0x62 } }
 
-// This enum defines type of content that is blocked when an
-// nsMixedContentBlockedEvent fires
-enum MixedContentBlockedTypes {
+// This enum defines type of content that is detected when an
+// nsMixedContentEvent fires
+enum MixedContentTypes {
   // "Active" content, such as fonts, plugin content, JavaScript, stylesheets,
   // iframes, WebSockets, and XHR
-  eBlockedMixedScript,
+  eMixedScript,
   // "Display" content, such as images, audio, video, and <a ping>
-  eBlockedMixedDisplay
+  eMixedDisplay
 };
 
 #include "nsIContentPolicy.h"
 
 class nsMixedContentBlocker : public nsIContentPolicy
 {
 public:
   NS_DECL_ISUPPORTS
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -17,17 +17,16 @@
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIExternalProtocolHandler.h"
 #include "nsEventStates.h"
 #include "nsIObjectFrame.h"
-#include "nsIPluginDocument.h"
 #include "nsIPermissionManager.h"
 #include "nsPluginHost.h"
 #include "nsIPresShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamConverterService.h"
 #include "nsIURILoader.h"
 #include "nsIURL.h"
@@ -527,16 +526,45 @@ IsPluginEnabledForType(const nsCString& 
 
   return NS_OK;
 }
 
 ///
 /// Member Functions
 ///
 
+// Tedious syntax to create a plugin stream listener with checks and put it in
+// mFinalListener
+bool
+nsObjectLoadingContent::MakePluginListener()
+{
+  if (!mInstanceOwner) {
+    NS_NOTREACHED("expecting a spawned plugin");
+    return false;
+  }
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+  if (!pluginHost) {
+    NS_NOTREACHED("No pluginHost");
+    return false;
+  }
+  NS_ASSERTION(!mFinalListener, "overwriting a final listener");
+  nsresult rv;
+  nsRefPtr<nsNPAPIPluginInstance> inst;
+  nsCOMPtr<nsIStreamListener> finalListener;
+  rv = mInstanceOwner->GetInstance(getter_AddRefs(inst));
+  NS_ENSURE_SUCCESS(rv, false);
+  rv = pluginHost->NewPluginStreamListener(mURI, inst,
+                                           getter_AddRefs(finalListener));
+  NS_ENSURE_SUCCESS(rv, false);
+  mFinalListener = finalListener;
+  return true;
+}
+
+
 bool
 nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
   nsCOMPtr<nsIWebNavigationInfo> info(
@@ -651,22 +679,28 @@ nsObjectLoadingContent::~nsObjectLoading
     // object...
     NS_NOTREACHED("Should not be tearing down a plugin at this point!");
     StopPluginInstance();
   }
   DestroyImageLoadingContent();
 }
 
 nsresult
-nsObjectLoadingContent::InstantiatePluginInstance()
+nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
 {
-  if (mInstanceOwner || mType != eType_Plugin || mIsLoading || mInstantiating) {
+  if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
+      mInstantiating) {
+    // If we hit this assertion it's probably because LoadObject re-entered :(
+    //
+    // XXX(johns): This hackiness will go away in bug 767635
+    NS_ASSERTION(mIsLoading || !aIsLoading,
+                 "aIsLoading should only be true inside LoadObject");
     return NS_OK;
   }
-  
+
   mInstantiating = true;
   AutoSetInstantiatingToFalse autoInstantiating(this);
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
 
   nsIDocument* doc = thisContent->GetCurrentDoc();
   if (!doc || !InActiveDocument(thisContent)) {
@@ -678,22 +712,22 @@ nsObjectLoadingContent::InstantiatePlugi
   // Instantiating an instance can result in script execution, which
   // can destroy this DOM object. Don't allow that for the scope
   // of this method.
   nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
 
   // Flush layout so that the frame is created if possible and the plugin is
   // initialized with the latest information.
   doc->FlushPendingNotifications(Flush_Layout);
-  
+
   if (!thisContent->GetPrimaryFrame()) {
     LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this));
     return NS_OK;
   }
-  
+
   nsresult rv = NS_ERROR_FAILURE;
   nsRefPtr<nsPluginHost> pluginHost =
     already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
 
   if (!pluginHost) {
     NS_NOTREACHED("No pluginhost");
     return NS_ERROR_FAILURE;
   }
@@ -701,36 +735,19 @@ nsObjectLoadingContent::InstantiatePlugi
   // If you add early return(s), be sure to balance this call to
   // appShell->SuspendNative() with additional call(s) to
   // appShell->ReturnNative().
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   if (appShell) {
     appShell->SuspendNative();
   }
 
-  nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(doc));
-  bool fullPageMode = false;
-  if (pDoc) {
-    pDoc->GetWillHandleInstantiation(&fullPageMode);
-  }
-
-  if (fullPageMode) {
-    nsCOMPtr<nsIStreamListener> stream;
-    rv = pluginHost->InstantiateFullPagePluginInstance(mContentType.get(),
-                                                       mURI.get(), this,
-                                                       getter_AddRefs(mInstanceOwner),
-                                                       getter_AddRefs(stream));
-    if (NS_SUCCEEDED(rv)) {
-      pDoc->SetStreamListener(stream);
-    }
-  } else {
-    rv = pluginHost->InstantiateEmbeddedPluginInstance(mContentType.get(),
-                                                       mURI.get(), this,
-                                                       getter_AddRefs(mInstanceOwner));
-  }
+  rv = pluginHost->InstantiatePluginInstance(mContentType.get(),
+                                             mURI.get(), this,
+                                             getter_AddRefs(mInstanceOwner));
 
   if (appShell) {
     appShell->ResumeNative();
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -758,16 +775,29 @@ nsObjectLoadingContent::InstantiatePlugi
         nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
                                                      NS_LITERAL_STRING("PluginOutdated"));
         nsresult rv = NS_DispatchToCurrentThread(ev);
         if (NS_FAILED(rv)) {
           NS_WARNING("failed to dispatch nsSimplePluginEvent");
         }
       }
     }
+
+    // If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
+    // or we did load with a channel but are re-instantiating, re-open the
+    // channel. OpenChannel() performs security checks, and this plugin has
+    // already passed content policy in LoadObject.
+    if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
+      NS_ASSERTION(!mChannel, "should not have an existing channel here");
+      if (MakePluginListener()) {
+        // We intentionally ignore errors here, leaving it up to the plugin to
+        // deal with not having an initial stream.
+        OpenChannel();
+      }
+    }
   }
 
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
 {
@@ -783,29 +813,42 @@ nsObjectLoadingContent::NotifyOwnerDocum
   }
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
-  /// This must call LoadObject, even upon failure, to allow it to either
-  /// proceed with the load, or trigger fallback content.
-
   SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest");
 
   LOG(("OBJLC [%p]: Channel OnStartRequest", this));
 
   if (aRequest != mChannel || !aRequest) {
     // happens when a new load starts before the previous one got here
     return NS_BINDING_ABORTED;
   }
 
   NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
+  // If we already switched to type plugin, this channel can just be passed to
+  // the final listener.
+  if (mType == eType_Plugin) {
+    if (!mInstanceOwner || !mFinalListener) {
+      // We drop mChannel when stopping plugins, so something is wrong
+      NS_NOTREACHED("Opened a channel in plugin mode, but don't have a plugin");
+      return NS_BINDING_ABORTED;
+    }
+    return mFinalListener->OnStartRequest(aRequest, nullptr);
+  }
+
+  // Otherwise we should be state loading, and call LoadObject with the channel
+  if (mType != eType_Loading) {
+    NS_NOTREACHED("Should be type loading at this point");
+    return NS_BINDING_ABORTED;
+  }
   NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
 
   mChannelLoaded = true;
 
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   NS_ASSERTION(chan, "Why is our request not a channel?");
 
   nsCOMPtr<nsIURI> uri;
@@ -1311,17 +1354,21 @@ nsObjectLoadingContent::UpdateObjectPara
   ///
   /// If we have a channel, see if its MIME type should take precendence and
   /// check the final (redirected) URL
   ///
 
   // If we have a loaded channel and channel parameters did not change, use it
   // to determine what we would load.
   bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
-  if (mChannel && useChannel) {
+  // If we have a channel and are type loading, as opposed to having an existing
+  // channel for a previous load.
+  bool newChannel = useChannel && mType == eType_Loading;
+
+  if (newChannel && mChannel) {
     nsCString channelType;
     rv = mChannel->GetContentType(channelType);
     if (NS_FAILED(rv)) {
       NS_NOTREACHED("GetContentType failed");
       stateInvalid = true;
       channelType.Assign("");
     }
 
@@ -1339,111 +1386,134 @@ nsObjectLoadingContent::UpdateObjectPara
 
     // Channel can change our URI through redirection
     rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
     if (NS_FAILED(rv)) {
       NS_NOTREACHED("NS_GetFinalChannelURI failure");
       stateInvalid = true;
     }
 
-    // The channel type overrides the guessed / provided type, except when:
+    ObjectType typeHint = newMime.IsEmpty() ?
+                          eType_Null : GetTypeOfContent(newMime);
+
+    //
+    // In order of preference:
     //
-    // 1) If the channel returns a binary stream type, and we have a type hint
-    //    for a non-document (we never want to display binary-as-document),
-    //    use our type hint instead.
-    // 2) Our type hint is a type that we support with a plugin, ignore the
-    //    server's type
+    // 1) Use our type hint if it matches a plugin
+    // 2) If we have eAllowPluginSkipChannel, use the uri file extension if
+    //    it matches a plugin
+    // 3) If the channel returns a binary stream type:
+    //    3a) If we have a type non-null non-document type hint, use that
+    //    3b) If the uri file extension matches a plugin type, use that
+    // 4) Use the channel type
     //
     //    XXX(johns): HTML5's "typesmustmatch" attribute would need to be
     //                honored here if implemented
 
-    ObjectType typeHint = newMime.IsEmpty() ? eType_Null : GetTypeOfContent(newMime);
-
-    bool caseOne = binaryChannelType
-                   && typeHint != eType_Null
-                   && typeHint != eType_Document;
-    bool caseTwo = typeHint == eType_Plugin;
-    if (caseOne || caseTwo) {
-        // Set the type we'll use for dispatch on the channel.  Otherwise we could
-        // end up trying to dispatch to a nsFrameLoader, which will complain that
-        // it couldn't find a way to handle application/octet-stream
-        nsAutoCString typeHint, dummy;
-        NS_ParseContentType(newMime, typeHint, dummy);
-        if (!typeHint.IsEmpty()) {
-          mChannel->SetContentType(typeHint);
-        }
-    } else if (binaryChannelType
-               && IsPluginEnabledByExtension(newURI, newMime)) {
-      mChannel->SetContentType(newMime);
+    bool overrideChannelType = false;
+    if (typeHint == eType_Plugin) {
+      LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
+           this));
+      overrideChannelType = true;
+    } else if ((caps & eAllowPluginSkipChannel) &&
+               IsPluginEnabledByExtension(newURI, newMime)) {
+      LOG(("OBJLC [%p]: Using extension as type hint for "
+           "eAllowPluginSkipChannel tag (%s)", this, newMime.get()));
+      overrideChannelType = true;
+    } else if (binaryChannelType &&
+               typeHint != eType_Null && typeHint != eType_Document) {
+      LOG(("OBJLC [%p]: Using type hint in favor of binary channel type",
+           this));
+      overrideChannelType = true;
+    } else if (binaryChannelType &&
+               IsPluginEnabledByExtension(newURI, newMime)) {
+      LOG(("OBJLC [%p]: Using extension as type hint for binary channel (%s)",
+           this, newMime.get()));
+      overrideChannelType = true;
+    }
+
+    if (overrideChannelType) {
+      // Set the type we'll use for dispatch on the channel.  Otherwise we could
+      // end up trying to dispatch to a nsFrameLoader, which will complain that
+      // it couldn't find a way to handle application/octet-stream
+      nsAutoCString parsedMime, dummy;
+      NS_ParseContentType(newMime, parsedMime, dummy);
+      if (!parsedMime.IsEmpty()) {
+        mChannel->SetContentType(parsedMime);
+      }
     } else {
       newMime = channelType;
       if (nsPluginHost::IsJavaMIMEType(newMime.get())) {
-        //   Java does not load with a channel, and being java retroactively changes
-        //   how we may have interpreted the codebase to construct this URI above.
-        //   Because the behavior here is more or less undefined, play it safe and
-        //   reject the load.
+        // Java does not load with a channel, and being java retroactively
+        // changes how we may have interpreted the codebase to construct this
+        // URI above.  Because the behavior here is more or less undefined, play
+        // it safe and reject the load.
         LOG(("OBJLC [%p]: Refusing to load with channel with java MIME",
              this));
         stateInvalid = true;
       }
     }
-  }
-
-  if (useChannel && !mChannel) {
-    // - (useChannel && !mChannel) is true if a channel was opened but
-    //   is no longer around, in which case we can't load.
+  } else if (newChannel) {
+    LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
     stateInvalid = true;
   }
 
   ///
   /// Determine final type
   ///
+  // In order of preference:
   //  1) If we have attempted channel load, or set stateInvalid above, the type
   //     is always null (fallback)
-  //  2) Otherwise, If we have a loaded channel, we grabbed its mimeType above,
-  //     use that type.
-  //  3) Otherwise, See if we can load this as a plugin without a channel
-  //     (image/document types always need a channel).
-  //     - If we have indication this is a plugin (mime, extension)
-  //       AND:
-  //       - We have eAllowPluginSkipChannel OR
-  //       - We have no URI in the first place (including java)
-  //  3) Otherwise, if we have a URI, set type to loading to indicate
-  //     we'd need a channel to proceed.
-  //  4) Otherwise, type null to indicate unloadable content (fallback)
-  //
-  // XXX(johns): <embed> tags both support URIs and have
-  //   eAllowPluginSkipChannel, meaning it is possible that we have a URI, but
-  //   are not going to open a channel for it. The old objLC code did this (in a
-  //   less obviously-intended way), so it's probably best not to change our
-  //   behavior at this point.
+  //  2) If we have a loaded channel, we grabbed its mimeType above, use that
+  //     type.
+  //  3) If we have a plugin type and no URI, use that type.
+  //  4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
+  //  5) if we have a URI, set type to loading to indicate we'd need a channel
+  //     to proceed.
+  //  6) Otherwise, type null to indicate unloadable content (fallback)
   //
 
   if (stateInvalid) {
     newType = eType_Null;
     newMime.Truncate();
-  } else if (useChannel) {
-      // If useChannel is set above, we considered it in setting newMime
+  } else if (newChannel) {
+      // If newChannel is set above, we considered it in setting newMime
       newType = GetTypeOfContent(newMime);
       LOG(("OBJLC [%p]: Using channel type", this));
   } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
-             (GetTypeOfContent(newMime) == eType_Plugin)) {
+             GetTypeOfContent(newMime) == eType_Plugin) {
     newType = eType_Plugin;
-    LOG(("OBJLC [%p]: Skipping loading channel, type plugin", this));
+    LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
   } else if (newURI) {
     // We could potentially load this if we opened a channel on mURI, indicate
     // This by leaving type as loading
     newType = eType_Loading;
   } else {
     // Unloadable - no URI, and no plugin type. Non-plugin types (images,
     // documents) always load with a channel.
     newType = eType_Null;
   }
 
   ///
+  /// Handle existing channels
+  ///
+
+  if (useChannel && newType == eType_Loading) {
+    // We decided to use a channel, and also that the previous channel is still
+    // usable, so re-use the existing values.
+    newType = mType;
+    newMime = mContentType;
+    newURI = mURI;
+  } else if (useChannel && !newChannel) {
+    // We have an existing channel, but did not decide to use one.
+    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
+    useChannel = false;
+  }
+
+  ///
   /// Update changed values
   ///
 
   if (newType != mType) {
     retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
     LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
     mType = newType;
   }
@@ -1472,19 +1542,54 @@ nsObjectLoadingContent::UpdateObjectPara
   if (mType != eType_Loading && mContentType != newMime) {
     retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
     retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
     LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)",
          this, mContentType.get(), newMime.get()));
     mContentType = newMime;
   }
 
+  // If we decided to keep using info from an old channel, but also that state
+  // changed, we need to invalidate it.
+  if (useChannel && !newChannel && (retval & eParamStateChanged)) {
+    mType = eType_Loading;
+    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
+  }
+
   return retval;
 }
 
+// Used by PluginDocument to kick off our initial load from the already-opened
+// channel.
+NS_IMETHODIMP
+nsObjectLoadingContent::InitializeFromChannel(nsIRequest *aChannel)
+{
+  LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
+  if (mType != eType_Loading || mChannel) {
+    // We could technically call UnloadObject() here, if consumers have a valid
+    // reason for wanting to call this on an already-loaded tag.
+    NS_NOTREACHED("Should not have begun loading at this point");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // Because we didn't open this channel from an initial LoadObject, we'll
+  // update our parameters now, so the OnStartRequest->LoadObject doesn't
+  // believe our src/type suddenly changed.
+  UpdateObjectParameters();
+  // But we always want to load from a channel, in this case.
+  mType = eType_Loading;
+  mChannel = do_QueryInterface(aChannel);
+  NS_ASSERTION(mChannel, "passed a request that is not a channel");
+
+  // OnStartRequest will now see we have a channel in the loading state, and
+  // call into LoadObject. There's a possibility LoadObject will decide not to
+  // load anything from a channel - it will call CloseChannel() in that case.
+  return NS_OK;
+}
+
 // Only OnStartRequest should be passing the channel parameter
 nsresult
 nsObjectLoadingContent::LoadObject(bool aNotify,
                                    bool aForceLoad)
 {
   return LoadObject(aNotify, aForceLoad, nullptr);
 }
 
@@ -1581,17 +1686,17 @@ nsObjectLoadingContent::LoadObject(bool 
   UnloadObject(false); // Don't reset state
   if (!mIsLoading) {
     // The event loop must've spun and re-entered into LoadObject, which
     // finished the load
     LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
     return NS_OK;
   }
 
-  // Determine what's going on with our channel
+  // Determine what's going on with our channel.
   if (stateChange & eParamChannelChanged) {
     // If the channel params changed, throw away the channel, but unset
     // mChannelLoaded so we'll still try to open a new one for this load if
     // necessary
     CloseChannel();
     mChannelLoaded = false;
   } else if (mType == eType_Null && mChannel) {
     // If we opened a channel but then failed to find a loadable state, throw it
@@ -1611,40 +1716,35 @@ nsObjectLoadingContent::LoadObject(bool 
   }
 
   //
   // Security checks
   //
 
   if (mType != eType_Null) {
     int16_t contentPolicy = nsIContentPolicy::ACCEPT;
-    bool allowLoad = false;
-    // We check load policy before opening a channel, and process policy before
-    // going ahead with any final-type load
-    if (mType == eType_Loading) {
-      nsCOMPtr<nsIScriptSecurityManager> secMan =
-        nsContentUtils::GetSecurityManager();
-      if (!secMan) {
-        NS_NOTREACHED("No security manager?");
-      } else {
-        rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(),
-                                               mURI, 0);
-        allowLoad = NS_SUCCEEDED(rv) && CheckLoadPolicy(&contentPolicy);
-      }
-    } else {
+    bool allowLoad = true;
+    // If mChannelLoaded is set we presumably already passed load policy
+    if (mURI && !mChannelLoaded) {
+      allowLoad = CheckLoadPolicy(&contentPolicy);
+    }
+    // If we're loading a type now, check ProcessPolicy. Note that we may check
+    // both now in the case of plugins whose type is determined before opening a
+    // channel.
+    if (allowLoad && mType != eType_Loading) {
       allowLoad = CheckProcessPolicy(&contentPolicy);
     }
 
     // Content policy implementations can mutate the DOM, check for re-entry
     if (!mIsLoading) {
       LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
            this));
       return NS_OK;
     }
-    
+
     // Load denied, switch to fallback and set disabled/suppressed if applicable
     if (!allowLoad) {
       LOG(("OBJLC [%p]: Load denied by policy", this));
       mType = eType_Null;
       if (contentPolicy == nsIContentPolicy::REJECT_TYPE) {
         // XXX(johns) This is assuming that we were rejected by
         //            nsContentBlocker, which rejects by type if permissions
         //            reject plugins
@@ -1700,78 +1800,72 @@ nsObjectLoadingContent::LoadObject(bool 
 
   ///
   /// Attempt to load new type
   ///
 
   // We don't set mFinalListener until OnStartRequest has been called, to
   // prevent re-entry ugliness with CloseChannel()
   nsCOMPtr<nsIStreamListener> finalListener;
+  // If we decide to synchronously spawn a plugin, we do it after firing
+  // notifications to avoid re-entry causing notifications to fire out of order.
+  bool doSpawnPlugin = false;
   switch (mType) {
     case eType_Image:
       if (!mChannel) {
         // We have a LoadImage() call, but UpdateObjectParameters requires a
         // channel for images, so this is not a valid state.
         NS_NOTREACHED("Attempting to load image without a channel?");
         rv = NS_ERROR_UNEXPECTED;
         break;
       }
       rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
       // finalListener will receive OnStartRequest below
     break;
     case eType_Plugin:
     {
       if (mChannel) {
-        nsRefPtr<nsPluginHost> pluginHost =
-          already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
-        if (!pluginHost) {
-          NS_NOTREACHED("No pluginHost");
-          rv = NS_ERROR_UNEXPECTED;
-          break;
-        }
-
         // Force a sync state change now, we need the frame created
         NotifyStateChanged(oldType, oldState, true, aNotify);
         oldType = mType;
         oldState = ObjectState();
 
         if (!thisContent->GetPrimaryFrame()) {
           // We're un-rendered, and can't instantiate a plugin. HasNewFrame will
           // re-start us when we can proceed.
           LOG(("OBJLC [%p]: Aborting load - plugin-type, but no frame", this));
           CloseChannel();
           break;
         }
-        
-        rv = pluginHost->NewEmbeddedPluginStreamListener(mURI, this, nullptr,
-                                                         getter_AddRefs(finalListener));
-        // finalListener will receive OnStartRequest below
+
+        // We'll handle this below
+        doSpawnPlugin = true;
       } else {
         rv = AsyncStartPluginInstance();
       }
     }
     break;
     case eType_Document:
     {
       if (!mChannel) {
         // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
         // requires documents have a channel, so this is not a valid state.
         NS_NOTREACHED("Attempting to load a document without a channel");
         mType = eType_Null;
         break;
       }
-      
+
       mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
                                            mNetworkCreated);
       if (!mFrameLoader) {
         NS_NOTREACHED("nsFrameLoader::Create failed");
         mType = eType_Null;
         break;
       }
-      
+
       rv = mFrameLoader->CheckForRecursiveLoad(mURI);
       if (NS_FAILED(rv)) {
         LOG(("OBJLC [%p]: Aborting recursive load", this));
         mFrameLoader->Destroy();
         mFrameLoader = nullptr;
         mType = eType_Null;
         break;
       }
@@ -1833,58 +1927,69 @@ nsObjectLoadingContent::LoadObject(bool 
     NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
                  "switched to type null but also loaded something");
 
     if (mChannel) {
       // If we were loading with a channel but then failed over, throw it away
       CloseChannel();
     }
 
-    // Don't try to initialize final listener below
+    // Don't try to initialize plugins or final listener below
+    doSpawnPlugin = false;
     finalListener = nullptr;
 
     // Don't notify, as LoadFallback doesn't know of our previous state
     // (so really this is just setting mFallbackType)
     LoadFallback(fallbackType, false);
   }
 
   // Notify of our final state
   NotifyStateChanged(oldType, oldState, false, aNotify);
+  NS_ENSURE_TRUE(mIsLoading, NS_OK);
+
 
   //
-  // Pass load on to finalListener if loading with a channel
+  // Spawning plugins and dispatching to the final listener may re-enter, so are
+  // delayed until after we fire a notification, to prevent missing
+  // notifications or firing them out of order.
+  //
+  // Note that we ensured that we entered into LoadObject() from
+  // ::OnStartRequest above when loading with a channel.
   //
 
-  if (!mIsLoading) {
-    LOG(("OBJLC [%p]: Re-entered before dispatching to final listener", this));
+  rv = NS_OK;
+  if (doSpawnPlugin) {
+    rv = InstantiatePluginInstance(true);
+    NS_ENSURE_TRUE(mIsLoading, NS_OK);
+    // Create the final listener if we're loading with a channel. We can't do
+    // this in the loading block above as it requires an instance.
+    if (aLoadingChannel && NS_SUCCEEDED(rv)) {
+      // Plugins can continue to run even if their initial stream dies. Some
+      // plugins will even return failure codes to reject the stream, but expect
+      // to continue running, so ignore the error code. rv is thus the result of
+      // spawning the plugin above
+      if (NS_SUCCEEDED(rv) && MakePluginListener()) {
+        mFinalListener->OnStartRequest(mChannel, nullptr);
+      }
+    }
   } else if (finalListener) {
     NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
                  "We should not have a final listener with a non-loaded type");
-    // Note that we always enter into LoadObject() from ::OnStartRequest when
-    // loading with a channel.
-    mSrcStreamLoading = true;
-    // Remove blocker on entering into instantiate
-    // (this is otherwise unset by the stack class)
-    mIsLoading = false;
     mFinalListener = finalListener;
     rv = finalListener->OnStartRequest(mChannel, nullptr);
-    mSrcStreamLoading = false;
-    if (NS_FAILED(rv)) {
-      // Failed to load new content, but since we've already notified of our
-      // transition, we can just Unload and call LoadFallback (which will notify
-      // again)
-      mType = eType_Null;
-      // This could *also* technically re-enter if OnStartRequest fails after
-      // spawning a plugin.
-      mIsLoading = true;
-      UnloadObject(false);
-      NS_ENSURE_TRUE(mIsLoading, NS_OK);
-      CloseChannel();
-      LoadFallback(fallbackType, true);
-    }
+  }
+
+  if (NS_FAILED(rv) && mIsLoading) {
+    // Since we've already notified of our transition, we can just Unload and
+    // call LoadFallback (which will notify again)
+    mType = eType_Null;
+    UnloadObject(false);
+    NS_ENSURE_TRUE(mIsLoading, NS_OK);
+    CloseChannel();
+    LoadFallback(fallbackType, true);
   }
 
   return NS_OK;
 }
 
 // This call can re-enter when dealing with plugin listeners
 nsresult
 nsObjectLoadingContent::CloseChannel()
@@ -1904,32 +2009,35 @@ nsObjectLoadingContent::CloseChannel()
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsObjectLoadingContent::OpenChannel()
 {
-  nsCOMPtr<nsIContent> thisContent = 
+  nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIScriptSecurityManager> secMan =
+    nsContentUtils::GetSecurityManager();
   NS_ASSERTION(thisContent, "must be a content");
   nsIDocument* doc = thisContent->OwnerDoc();
   NS_ASSERTION(doc, "No owner document?");
-  NS_ASSERTION(!mInstanceOwner && !mInstantiating,
-               "opening a new channel with already loaded content");
 
   nsresult rv;
   mChannel = nullptr;
 
   // E.g. mms://
   if (!mURI || !CanHandleURI(mURI)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), mURI, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> chan;
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, rv);
   if (csp) {
     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -111,20 +111,23 @@ class nsObjectLoadingContent : public ns
     ObjectType Type() const { return mType; }
 
     void SetIsNetworkCreated(bool aNetworkCreated)
     {
       mNetworkCreated = aNetworkCreated;
     }
 
     /**
-     * Immediately instantiate a plugin instance. This is a no-op if
-     * mType != eType_Plugin or a plugin is already running.
+     * Immediately instantiate a plugin instance. This is a no-op if mType !=
+     * eType_Plugin or a plugin is already running.
+     *
+     * aIsLoading indicates that we are in the loading code, and we can bypass
+     * the mIsLoading check.
      */
-    nsresult InstantiatePluginInstance();
+    nsresult InstantiatePluginInstance(bool aIsLoading = false);
 
     /**
      * Notify this class the document state has changed
      * Called by nsDocument so we may suspend plugins in inactive documents)
      */
     void NotifyOwnerDocumentActivityChanged();
 
     /**
@@ -175,22 +178,21 @@ class nsObjectLoadingContent : public ns
       eSupportImages       = 1u << 0, // Images are supported (imgILoader)
       eSupportPlugins      = 1u << 1, // Plugins are supported (nsIPluginHost)
       eSupportDocuments    = 1u << 2, // Documents are supported
                                         // (nsIDocumentLoaderFactory)
                                         // This flag always includes SVG
       eSupportSVG          = 1u << 3, // SVG is supported (image/svg+xml)
       eSupportClassID      = 1u << 4, // The classid attribute is supported
 
-      // Allows us to load a plugin if it matches a MIME type or file extension
-      // registered to a plugin without opening its specified URI first. Can
-      // result in launching plugins for URIs that return differing content
-      // types. Plugins without URIs may instantiate regardless.
-      // XXX(johns) this is our legacy behavior on <embed> tags, whereas object
-      // will always open a channel and check its MIME if a URI is present.
+      // If possible to get a *plugin* type from the type attribute *or* file
+      // extension, we can use that type and begin loading the plugin before
+      // opening a channel.
+      // A side effect of this is if the channel fails, the plugin is still
+      // running.
       eAllowPluginSkipChannel  = 1u << 5
     };
 
     /**
      * Returns the list of capabilities this content node supports. This is a
      * bitmask consisting of flags from the Capabilities enum.
      *
      * The default implementation supports all types but not
@@ -213,16 +215,17 @@ class nsObjectLoadingContent : public ns
 
     nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                         nsIContent* aBindingParent,
                         bool aCompileEventHandler);
     void UnbindFromTree(bool aDeep = true,
                         bool aNullParent = true);
 
   private:
+
     // Object parameter changes returned by UpdateObjectParameters
     enum ParameterUpdateFlags {
       eParamNoChange           = 0,
       // Parameters that potentially affect the channel changed
       // - mOriginalURI, mOriginalContentType
       eParamChannelChanged     = 1u << 0,
       // Parameters that affect displayed content changed
       // - mURI, mContentType, mType, mBaseURI
@@ -324,16 +327,22 @@ class nsObjectLoadingContent : public ns
     /**
      * Checks whether the given type is a supported document type
      *
      * NOTE Does not take content policy or capabilities into account
      */
     bool IsSupportedDocument(const nsCString& aType);
 
     /**
+     * Gets the plugin instance and creates a plugin stream listener, assigning
+     * it to mFinalListener
+     */
+    bool MakePluginListener();
+
+    /**
      * Unloads all content and resets the object to a completely unloaded state
      *
      * NOTE Calls StopPluginInstance() and may spin the event loop
      *
      * @param aResetState Reset the object type to 'loading' and destroy channel
      *                    as well
      */
     void UnloadObject(bool aResetState = true);
@@ -406,18 +415,19 @@ class nsObjectLoadingContent : public ns
 
 
 
     // Type of the currently-loaded content.
     ObjectType                  mType           : 8;
     // The type of fallback content we're showing (see ObjectState())
     FallbackType                mFallbackType : 8;
 
-    // If true, the current load has finished opening a channel. Does not imply
-    // mChannel -- mChannelLoaded && !mChannel may occur for a load that failed
+    // If true, we have opened a channel as the listener and it has reached
+    // OnStartRequest. Does not get set for channels that are passed directly to
+    // the plugin listener.
     bool                        mChannelLoaded    : 1;
 
     // Whether we are about to call instantiate on our frame. If we aren't,
     // SetFrame needs to asynchronously call Instantiate.
     bool                        mInstantiating : 1;
 
     // True when the object is created for an element which the parser has
     // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -590,16 +590,17 @@ MOCHITEST_FILES_B = \
     test_mixed_content_blocker_bug803225.html \
     file_mixed_content_main_bug803225.html \
     file_mixed_content_main_bug803225_websocket_wsh.py \
     bug803225_test_mailto.html \
 		test_bug789856.html \
 		file_bug804395.jar \
 		test_bug804395.html \
 		test_bug809003.html \
+		test_bug810494.html \
 		test_textnode_split_in_selection.html \
 		test_textnode_normalize_in_selection.html \
 		test_xhr_send_readystate.html \
 		test_bug813919.html \
 		test_bug814576.html \
 		test_xhr_withCredentials.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug810494.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=810494
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 810494</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=810494">Mozilla Bug 810494</a>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+function test(tag, type) {
+  "use strict";
+  info("testing " + tag + " tag with type " + type);
+
+  const OBJLC = Components.interfaces.nsIObjectLoadingContent;
+  let obj = document.createElement(tag);
+  obj.type = type;
+  document.body.appendChild(obj);
+
+  obj instanceof OBJLC;
+  obj = SpecialPowers.wrap(obj);
+
+  // We expect this tag to simply go to alternate content, not get a
+  // pluginProblem binding or fire any events.
+  ok(obj.displayedType == OBJLC.TYPE_NULL, "expected null type");
+  ok(obj.pluginFallbackType == OBJLC.PLUGIN_ALTERNATE,
+     "expected alternate fallback mode");
+}
+
+// Test all non-plugin types these tags can load to make sure none of them
+// trigger plugin-specific fallbacks when loaded with no URI
+test("object", "text/html");     // Document
+test("object", "image/png");     // Image
+test("object", "image/svg+xml"); // SVG Document
+
+test("embed", "image/png");      // Image
+test("embed", "image/svg+xml");  // SVG Document
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -8,16 +8,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsGkAtoms.h"
 #include "nsError.h"
 #include "nsIDocument.h"
+#include "nsIPluginDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMSVGDocument.h"
 #include "nsIDOMGetSVGDocument.h"
 #include "nsIDOMHTMLObjectElement.h"
 #include "nsFormSubmission.h"
 #include "nsIObjectFrame.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIConstraintValidation.h"
@@ -236,18 +237,23 @@ nsHTMLObjectElement::BindToTree(nsIDocum
                                                      aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
                                           aBindingParent,
                                           aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Don't kick off load from being bound to a plugin document - the plugin
+  // document will call nsObjectLoadingContent::InitializeFromChannel() for the
+  // initial load.
+  nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
+
   // If we already have all the children, start the load.
-  if (mIsDoneAddingChildren) {
+  if (mIsDoneAddingChildren && !pluginDoc) {
     void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad;
     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
   }
 
   return NS_OK;
 }
 
 void
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp
+++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/Util.h"
 
 #include "nsGenericHTMLElement.h"
 #include "nsObjectLoadingContent.h"
 #include "nsGkAtoms.h"
 #include "nsError.h"
 #include "nsIDocument.h"
+#include "nsIPluginDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLAppletElement.h"
 #include "nsIDOMHTMLEmbedElement.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMGetSVGDocument.h"
 #include "nsIDOMSVGDocument.h"
 #include "nsIScriptError.h"
 #include "nsIWidget.h"
@@ -264,18 +265,23 @@ nsHTMLSharedObjectElement::BindToTree(ns
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
                                           aBindingParent,
                                           aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Don't kick off load from being bound to a plugin document - the plugin
+  // document will call nsObjectLoadingContent::InitializeFromChannel() for the
+  // initial load.
+  nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
+
   // If we already have all the children, start the load.
-  if (mIsDoneAddingChildren) {
+  if (mIsDoneAddingChildren && !pluginDoc) {
     void (nsHTMLSharedObjectElement::*start)() =
       &nsHTMLSharedObjectElement::StartObjectLoad;
     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
   }
 
   return NS_OK;
 }
 
--- a/content/html/document/src/PluginDocument.cpp
+++ b/content/html/document/src/PluginDocument.cpp
@@ -40,115 +40,77 @@ public:
                                      nsIContentSink*     aSink = nullptr);
 
   virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject);
   virtual bool CanSavePresentation(nsIRequest *aNewRequest);
 
   const nsCString& GetType() const { return mMimeType; }
   nsIContent*      GetPluginContent() { return mPluginContent; }
 
-  void AllowNormalInstantiation() {
-    mWillHandleInstantiation = false;
-  }
-
   void StartLayout() { MediaDocument::StartLayout(); }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PluginDocument, MediaDocument)
 protected:
   nsresult CreateSyntheticPluginDocument();
 
   nsCOMPtr<nsIContent>                     mPluginContent;
   nsRefPtr<MediaDocumentStreamListener>    mStreamListener;
   nsCString                                mMimeType;
-
-  // Hack to handle the fact that plug-in loading lives in frames and that the
-  // frames may not be around when we need to instantiate.  Once plug-in
-  // loading moves to content, this can all go away.
-  bool                                     mWillHandleInstantiation;
 };
 
 class PluginStreamListener : public MediaDocumentStreamListener
 {
 public:
   PluginStreamListener(PluginDocument* doc)
     : MediaDocumentStreamListener(doc)
     , mPluginDoc(doc)
   {}
   NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt);
 private:
-  nsresult SetupPlugin();
-
   nsRefPtr<PluginDocument> mPluginDoc;
 };
 
 
 NS_IMETHODIMP
 PluginStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
 {
   SAMPLE_LABEL("PluginStreamListener", "OnStartRequest");
-  // Have to set up our plugin stuff before we call OnStartRequest, so
-  // that the plugin listener can get that call.
-  nsresult rv = SetupPlugin();
-
-  NS_ASSERTION(NS_FAILED(rv) || mNextStream,
-               "We should have a listener by now");
-  nsresult rv2 = MediaDocumentStreamListener::OnStartRequest(request, ctxt);
-  return NS_SUCCEEDED(rv) ? rv2 : rv;
-}
-
-nsresult
-PluginStreamListener::SetupPlugin()
-{
-  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
-  mPluginDoc->StartLayout();
 
   nsCOMPtr<nsIContent> embed = mPluginDoc->GetPluginContent();
+  nsCOMPtr<nsIObjectLoadingContent> objlc = do_QueryInterface(embed);
+  nsCOMPtr<nsIStreamListener> objListener = do_QueryInterface(objlc);
 
-  // Now we have a frame for our <embed>, start the load
-  nsCOMPtr<nsIPresShell> shell = mDocument->GetShell();
-  if (!shell) {
-    // Can't instantiate w/o a shell
-    mPluginDoc->AllowNormalInstantiation();
+  if (!objListener) {
+    NS_NOTREACHED("PluginStreamListener without appropriate content node");
     return NS_BINDING_ABORTED;
   }
 
-  // Flush out layout before we go to instantiate, because some
-  // plug-ins depend on NPP_SetWindow() being called early enough and
-  // nsObjectFrame does that at the end of reflow.
-  shell->FlushPendingNotifications(Flush_Layout);
+  SetStreamListener(objListener);
 
-  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(embed));
-  if (!olc) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  nsObjectLoadingContent* olcc = static_cast<nsObjectLoadingContent*>(olc.get());
-  nsresult rv = olcc->InstantiatePluginInstance();
+  // Sets up the ObjectLoadingContent tag as if it is waiting for a
+  // channel, so it can proceed with a load normally once it gets OnStartRequest
+  nsresult rv = objlc->InitializeFromChannel(request);
   if (NS_FAILED(rv)) {
+    NS_NOTREACHED("InitializeFromChannel failed");
     return rv;
   }
 
-  // Now that we're done, allow normal instantiation in the future
-  // (say if there's a reframe of this entire presentation).
-  mPluginDoc->AllowNormalInstantiation();
-
-  return NS_OK;
+  // Note that because we're now hooked up to a plugin listener, this will
+  // likely spawn a plugin, which may re-enter.
+  return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
 }
 
-
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 PluginDocument::PluginDocument()
-  : mWillHandleInstantiation(true)
-{
-}
+{}
 
 PluginDocument::~PluginDocument()
-{
-}
+{}
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PluginDocument)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PluginDocument, MediaDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPluginContent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PluginDocument, MediaDocument)
@@ -221,16 +183,18 @@ PluginDocument::StartDocumentLoad(const 
     return rv;
   }
 
   rv = aChannel->GetContentType(mMimeType);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  MediaDocument::UpdateTitleAndCharset(mMimeType);
+
   mStreamListener = new PluginStreamListener(this);
   if (!mStreamListener) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   NS_ASSERTION(aDocListener, "null aDocListener");
   NS_ADDREF(*aDocListener = mStreamListener);
 
   return rv;
@@ -285,38 +249,26 @@ PluginDocument::CreateSyntheticPluginDoc
   mDocumentURI->GetSpec(src);
   mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src,
                           NS_ConvertUTF8toUTF16(src), false);
 
   // set mime type
   mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
                           NS_ConvertUTF8toUTF16(mMimeType), false);
 
-  // This will not start the load because nsObjectLoadingContent checks whether
-  // its document is an nsIPluginDocument
+  // nsHTML(Shared)ObjectElement does not kick off a load on BindToTree if it is
+  // to a PluginDocument
   body->AppendChildTo(mPluginContent, false);
 
   return NS_OK;
 
 
 }
 
 NS_IMETHODIMP
-PluginDocument::SetStreamListener(nsIStreamListener *aListener)
-{
-  if (mStreamListener) {
-    mStreamListener->SetStreamListener(aListener);
-  }
-
-  MediaDocument::UpdateTitleAndCharset(mMimeType);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 PluginDocument::Print()
 {
   NS_ENSURE_TRUE(mPluginContent, NS_ERROR_FAILURE);
 
   nsIObjectFrame* objectFrame =
     do_QueryFrame(mPluginContent->GetPrimaryFrame());
   if (objectFrame) {
     nsRefPtr<nsNPAPIPluginInstance> pi;
@@ -330,23 +282,16 @@ PluginDocument::Print()
 
       pi->Print(&npprint);
     }
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-PluginDocument::GetWillHandleInstantiation(bool* aWillHandle)
-{
-  *aWillHandle = mWillHandleInstantiation;
-  return NS_OK;
-}
-
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewPluginDocument(nsIDocument** aResult)
 {
   mozilla::dom::PluginDocument* doc = new mozilla::dom::PluginDocument();
   if (!doc) {
--- a/content/media/AudioSampleFormat.h
+++ b/content/media/AudioSampleFormat.h
@@ -138,17 +138,17 @@ ScaleAudioSamples(float* aBuffer, int aC
     aBuffer[i] *= aScale;
   }
 }
 
 
 inline void
 ScaleAudioSamples(short* aBuffer, int aCount, float aScale)
 {
-  int32_t volume = int32_t(1 << 16) * aScale;
+  int32_t volume = int32_t((1 << 16) * aScale);
   for (int32_t i = 0; i < aCount; ++i) {
     aBuffer[i] = short((int32_t(aBuffer[i]) * volume) >> 16);
   }
 }
 
 } // namespace mozilla
 
 #endif /* MOZILLA_AUDIOSAMPLEFORMAT_H_ */
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -1623,16 +1623,20 @@ MediaCacheStream::NotifyDataStarted(int6
   NS_WARN_IF_FALSE(aOffset == mChannelOffset,
                    "Server is giving us unexpected offset");
   mChannelOffset = aOffset;
   if (mStreamLength >= 0) {
     // If we started reading at a certain offset, then for sure
     // the stream is at least that long.
     mStreamLength = NS_MAX(mStreamLength, mChannelOffset);
   }
+  // Ensure that |mDownloadCancelled| is set to false since we have new data.
+  if (mDownloadCancelled) {
+    mDownloadCancelled = false;
+  }
 }
 
 bool
 MediaCacheStream::UpdatePrincipal(nsIPrincipal* aPrincipal)
 {
   return nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
 }
 
@@ -1792,16 +1796,34 @@ MediaCacheStream::NotifyDataEnded(nsresu
       stream->mClient->CacheClientNotifyDataEnded(aStatus);
     }
   }
 
   mChannelEnded = true;
   gMediaCache->QueueUpdate();
 }
 
+void
+MediaCacheStream::NotifyDownloadCancelled()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+  ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
+
+  MediaCache::ResourceStreamIterator iter(mResourceID);
+  while (MediaCacheStream* stream = iter.Next()) {
+    // The remainder of the download was cancelled; in order to cancel any
+    // waiting reads, assume length is equal to current channel offset.
+    stream->mDownloadCancelled = true;
+  }
+
+  // Wake up waiting streams so they will stop reading.
+  mon.NotifyAll();
+}
+
 MediaCacheStream::~MediaCacheStream()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mPinCount, "Unbalanced Pin");
 
   if (gMediaCache) {
     NS_ASSERTION(mClosed, "Stream was not closed");
     gMediaCache->ReleaseStream(this);
@@ -2136,16 +2158,21 @@ MediaCacheStream::Read(char* aBuffer, ui
 
       // No data has been read yet, so block
       mon.Wait();
       if (mClosed) {
         // We may have successfully read some data, but let's just throw
         // that out.
         return NS_ERROR_FAILURE;
       }
+      // If the download was cancelled, stop reading and return silently.
+      if (mDownloadCancelled) {
+        mDownloadCancelled = false;
+        return NS_OK;
+      }
       continue;
     }
 
     gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
 
     int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
     NS_ABORT_IF_FALSE(size >= 0 && size <= INT32_MAX, "Size out of range.");
     nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, int32_t(size), &bytes);
--- a/content/media/MediaCache.h
+++ b/content/media/MediaCache.h
@@ -189,16 +189,17 @@ public:
     MODE_PLAYBACK
   };
 
   // aClient provides the underlying transport that cache will use to read
   // data for this stream.
   MediaCacheStream(ChannelMediaResource* aClient)
     : mClient(aClient), mInitialized(false),
       mHasHadUpdate(false),
+      mDownloadCancelled(false),
       mClosed(false),
       mDidNotifyDataEnded(false), mResourceID(0),
       mIsTransportSeekable(false), mCacheSuspended(false),
       mChannelEnded(false),
       mChannelOffset(0), mStreamLength(-1),  
       mStreamOffset(0), mPlaybackBytesPerSecond(10000),
       mPinCount(0), mCurrentMode(MODE_PLAYBACK),
       mMetadataInPartialBlockBuffer(false) {}
@@ -273,16 +274,23 @@ public:
   // We pass in the principal that was used to load this data.
   void NotifyDataReceived(int64_t aSize, const char* aData,
                           nsIPrincipal* aPrincipal);
   // Notifies the cache that the current bytes should be written to disk.
   // Called on the main thread.
   void FlushPartialBlock();
   // Notifies the cache that the channel has closed with the given status.
   void NotifyDataEnded(nsresult aStatus);
+  // Notifies the cache that the download was cancelled. Sets
+  // |mDownloadCancelled| to false and notifies any thread waiting on the media
+  // cache monitor. In this way, if |Read| is waiting when a download is
+  // cancelled, it will be wakened and should stop trying to read.
+  // Note: |mDownloadCancelled| is set to false when |Read| is awakened, and in
+  // |NotifyDataStarted|, when new data starts downloading.
+  void NotifyDownloadCancelled();
 
   // These methods can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
   // while the stream is pinned.
   void Pin();
   void Unpin();
   // See comments above for NotifyDataLength about how the length
   // can vary over time. Returns -1 if no length is known. Returns the
@@ -435,16 +443,19 @@ private:
   // These fields are main-thread-only.
   ChannelMediaResource*  mClient;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // Set to true when Init or InitAsClone has been called
   bool                   mInitialized;
   // Set to true when MediaCache::Update() has finished while this stream
   // was present.
   bool                   mHasHadUpdate;
+  // True if the download was cancelled. Set true in NotifyDownloadCancelled.
+  // Set false in NotifyDataStarted.
+  bool mDownloadCancelled;
   // Set to true when the stream has been closed either explicitly or
   // due to an internal cache error
   bool                   mClosed;
   // True if CacheClientNotifyDataEnded has been called for this stream.
   bool                   mDidNotifyDataEnded;
 
   // The following fields must be written holding the cache's monitor and
   // only on the main thread, thus can be read either on the main thread
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -278,16 +278,17 @@ double MediaDecoder::GetDuration()
   if (mDuration >= 0) {
      return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S);
   }
   return std::numeric_limits<double>::quiet_NaN();
 }
 
 int64_t MediaDecoder::GetMediaDuration()
 {
+  NS_ENSURE_TRUE(GetStateMachine(), -1);
   return GetStateMachine()->GetDuration();
 }
 
 void MediaDecoder::SetInfinite(bool aInfinite)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInfiniteStream = aInfinite;
 }
@@ -424,16 +425,17 @@ nsresult MediaDecoder::Load(MediaResourc
   }
 
   return InitializeStateMachine(aCloneDonor);
 }
 
 nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
 
   MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
   if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
                                            cloneDonor->mDecoderStateMachine : nullptr))) {
     LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
     return NS_ERROR_FAILURE;
   }
   {
@@ -920,16 +922,20 @@ void MediaDecoder::NotifySuspendedStatus
     mOwner->NotifySuspendedByCache(suspended);
     UpdateReadyStateForData();
   }
 }
 
 void MediaDecoder::NotifyBytesDownloaded()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  {
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    UpdatePlaybackRate();
+  }
   UpdateReadyStateForData();
   Progress(false);
 }
 
 void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -959,16 +965,17 @@ void MediaDecoder::NotifyPrincipalChange
 {
   if (mOwner) {
     mOwner->NotifyDecoderPrincipalChanged();
   }
 }
 
 void MediaDecoder::NotifyBytesConsumed(int64_t aBytes)
 {
+  NS_ENSURE_TRUE(mDecoderStateMachine, );
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(OnStateMachineThread() || mDecoderStateMachine->OnDecodeThread());
   if (!mIgnoreProgressData) {
     mDecoderPosition += aBytes;
     mPlaybackStatistics.AddBytes(aBytes);
   }
 }
 
@@ -1190,16 +1197,17 @@ void MediaDecoder::SetDuration(double aD
   }
 
   // Duration has changed so we should recompute playback rate
   UpdatePlaybackRate();
 }
 
 void MediaDecoder::SetMediaDuration(int64_t aDuration)
 {
+  NS_ENSURE_TRUE(GetStateMachine(), );
   GetStateMachine()->SetDuration(aDuration);
 }
 
 void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
   mMediaSeekable = aMediaSeekable;
   if (mDecoderStateMachine) {
@@ -1220,16 +1228,17 @@ void MediaDecoder::SetTransportSeekable(
 bool MediaDecoder::IsTransportSeekable()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mTransportSeekable;
 }
 
 bool MediaDecoder::IsMediaSeekable()
 {
+  NS_ENSURE_TRUE(GetStateMachine(), false);
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
   return mMediaSeekable;
 }
 
 nsresult MediaDecoder::GetSeekable(nsTimeRanges* aSeekable)
 {
   //TODO : change 0.0 to GetInitialTime() when available
@@ -1256,16 +1265,17 @@ void MediaDecoder::SetFragmentEndTime(do
   if (mDecoderStateMachine) {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
   }
 }
 
 void MediaDecoder::SetMediaEndTime(int64_t aTime)
 {
+  NS_ENSURE_TRUE(GetStateMachine(), );
   GetStateMachine()->SetMediaEndTime(aTime);
 }
 
 void MediaDecoder::Suspend()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mResource) {
     mResource->Suspend(true);
@@ -1358,16 +1368,17 @@ void MediaDecoder::SetPlaybackRate(doubl
 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
 {
   if (mDecoderStateMachine) {
     mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
   }
 }
 
 bool MediaDecoder::OnDecodeThread() const {
+  NS_ENSURE_TRUE(mDecoderStateMachine, false);
   return mDecoderStateMachine->OnDecodeThread();
 }
 
 ReentrantMonitor& MediaDecoder::GetReentrantMonitor() {
   return mReentrantMonitor.GetReentrantMonitor();
 }
 
 ImageContainer* MediaDecoder::GetImageContainer()
@@ -1417,20 +1428,22 @@ void MediaDecoder::UpdatePlaybackPositio
 }
 
 // Provide access to the state machine object
 MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
   return mDecoderStateMachine;
 }
 
 bool MediaDecoder::IsShutdown() const {
+  NS_ENSURE_TRUE(GetStateMachine(), true);
   return GetStateMachine()->IsShutdown();
 }
 
 int64_t MediaDecoder::GetEndMediaTime() const {
+  NS_ENSURE_TRUE(GetStateMachine(), -1);
   return GetStateMachine()->GetEndMediaTime();
 }
 
 // Drop reference to state machine.  Only called during shutdown dance.
 void MediaDecoder::ReleaseStateMachine() {
   mDecoderStateMachine = nullptr;
 }
 
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -613,20 +613,34 @@ public:
   void StopProgressUpdates();
 
   // Allow updating the bytes downloaded for progress notifications. Must
   // be called with the decoder monitor held.
   void StartProgressUpdates();
 
   // Something has changed that could affect the computed playback rate,
   // so recompute it. The monitor must be held.
-  void UpdatePlaybackRate();
+  virtual void UpdatePlaybackRate();
+
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity stopping on the channel. The monitor must be held.
+  virtual void NotifyPlaybackStarted() {
+    GetReentrantMonitor().AssertCurrentThreadIn();
+    mPlaybackStatistics.Start();
+  }
+
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity stopping on the channel. The monitor must be held.
+  virtual void NotifyPlaybackStopped() {
+    GetReentrantMonitor().AssertCurrentThreadIn();
+    mPlaybackStatistics.Stop();
+  }
 
   // The actual playback rate computation. The monitor must be held.
-  double ComputePlaybackRate(bool* aReliable);
+  virtual double ComputePlaybackRate(bool* aReliable);
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   bool CanPlayThrough();
 
   // Make the decoder state machine update the playback position. Called by
   // the reader on the decoder thread (Assertions for this checked by
   // mDecoderStateMachine). This must be called with the decode monitor
@@ -782,17 +796,17 @@ public:
     // a short time).
     bool mPlaybackRateReliable;
   };
 
   // Return statistics. This is used for progress events and other things.
   // This can be called from any thread. It's only a snapshot of the
   // current state, since other threads might be changing the state
   // at any time.
-  Statistics GetStatistics();
+  virtual Statistics GetStatistics();
 
   // Frame decoding/painting related performance counters.
   // Threadsafe.
   class FrameStatistics {
   public:
 
     FrameStatistics() :
         mReentrantMonitor("MediaDecoder::FrameStats"),
@@ -857,17 +871,17 @@ public:
     uint32_t mPresentedFrames;
   };
 
   // Return the frame decode/paint related statistics.
   FrameStatistics& GetFrameStatistics() { return mFrameStats; }
 
   // Increments the parsed and decoded frame counters by the passed in counts.
   // Can be called on any thread.
-  virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE
+  virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE
   {
     GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded);
   }
 
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
@@ -876,20 +890,16 @@ public:
   // seek operations, but it's updated at the end when we start playing
   // back again.
   int64_t mDecoderPosition;
   // Current playback position in the stream. This is (approximately)
   // where we're up to playing back the stream. This is not adjusted
   // during decoder seek operations, but it's updated at the end when we
   // start playing back again.
   int64_t mPlaybackPosition;
-  // Data needed to estimate playback data rate. The timeline used for
-  // this estimate is "decode time" (where the "current time" is the
-  // time of the last decoded video frame).
-  MediaChannelStatistics mPlaybackStatistics;
 
   // The current playback position of the media resource in units of
   // seconds. This is updated approximately at the framerate of the
   // video (if it is a video) or the callback period of the audio.
   // It is read and written from the main thread only.
   double mCurrentTime;
 
   // Volume that playback should start at.  0.0 = muted. 1.0 = full
@@ -1045,16 +1055,21 @@ protected:
 
   // Time that data was last read from the media resource. Used for
   // computing if the download has stalled and to rate limit progress events
   // when data is arriving slower than PROGRESS_MS. A value of null indicates
   // that a stall event has already fired and not to fire another one until
   // more data is received. Read/Write from the main thread only.
   TimeStamp mDataTime;
 
+  // Data needed to estimate playback data rate. The timeline used for
+  // this estimate is "decode time" (where the "current time" is the
+  // time of the last decoded video frame).
+  MediaChannelStatistics mPlaybackStatistics;
+
   // The framebuffer size to use for audioavailable events.
   uint32_t mFrameBufferLength;
 
   // True when our media stream has been pinned. We pin the stream
   // while seeking.
   bool mPinnedForSeek;
 
   // True if the decoder is being shutdown. At this point all events that
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1217,17 +1217,17 @@ nsresult MediaDecoderStateMachine::Init(
 void MediaDecoderStateMachine::StopPlayback()
 {
   LOG(PR_LOG_DEBUG, ("%p StopPlayback()", mDecoder.get()));
 
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine thread or the decoder thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
-  mDecoder->mPlaybackStatistics.Stop(TimeStamp::Now());
+  mDecoder->NotifyPlaybackStopped();
 
   if (IsPlaying()) {
     mPlayDuration += DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
     mPlayStartTime = TimeStamp();
   }
   // Notify the audio thread, so that it notices that we've stopped playing,
   // so it can pause audio playback.
   mDecoder->GetReentrantMonitor().NotifyAll();
@@ -1236,17 +1236,17 @@ void MediaDecoderStateMachine::StopPlayb
 
 void MediaDecoderStateMachine::StartPlayback()
 {
   LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
 
   NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   LOG(PR_LOG_DEBUG, ("%p StartPlayback", mDecoder.get()));
-  mDecoder->mPlaybackStatistics.Start(TimeStamp::Now());
+  mDecoder->NotifyPlaybackStarted();
   mPlayStartTime = TimeStamp::Now();
 
   NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
   if (NS_FAILED(StartAudioThread())) {
     NS_WARNING("Failed to create audio thread"); 
   }
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -320,17 +320,17 @@ ChannelMediaResource::OnStartRequest(nsI
     rv = cc->IsFromCache(&fromCache);
     if (NS_SUCCEEDED(rv) && !fromCache) {
       cc->SetCacheAsFile(true);
     }
   }
 
   {
     MutexAutoLock lock(mLock);
-    mChannelStatistics.Start(TimeStamp::Now());
+    mChannelStatistics->Start();
   }
 
   mReopenOnError = false;
   // If we are seeking to get metadata, because we are playing an OGG file,
   // ignore if the channel gets closed without us suspending it explicitly. We
   // don't want to tell the element that the download has finished whereas we
   // just happended to have reached the end of the media while seeking.
   mIgnoreClose = mSeekingForMetadata;
@@ -401,17 +401,17 @@ nsresult
 ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
   NS_ASSERTION(mSuspendCount == 0,
                "How can OnStopRequest fire while we're suspended?");
 
   {
     MutexAutoLock lock(mLock);
-    mChannelStatistics.Stop(TimeStamp::Now());
+    mChannelStatistics->Stop();
   }
 
   // If we were loading a byte range, notify decoder and return.
   // Skip this for unterminated byte range requests, e.g. seeking for whole
   // file downloads.
   if (mByteRangeDownloads) {
     mDecoder->NotifyDownloadEnded(aStatus);
     return NS_OK;
@@ -507,17 +507,17 @@ nsresult
 ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
                                       nsIInputStream* aStream,
                                       uint32_t aCount)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
 
   {
     MutexAutoLock lock(mLock);
-    mChannelStatistics.AddBytes(aCount);
+    mChannelStatistics->AddBytes(aCount);
   }
 
   CopySegmentClosure closure;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   if (secMan && mChannel) {
     secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
   }
   closure.mResource = this;
@@ -562,20 +562,36 @@ ChannelMediaResource::OpenByteRange(nsIS
   CloseChannel();
 
   nsresult rv = RecreateChannel();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return OpenChannel(aStreamListener);
 }
 
+void
+ChannelMediaResource::CancelByteRangeOpen()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+  // Byte range download will be cancelled in |CacheClientSeek|. Here, we only
+  // need to notify the cache to in turn notify any waiting reads.
+  if (mByteRangeDownloads) {
+    mCacheStream.NotifyDownloadCancelled();
+  }
+}
+
 nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
+  if (!mChannelStatistics) {
+    mChannelStatistics = new MediaChannelStatistics();
+  }
+
   nsresult rv = mCacheStream.Init();
   if (NS_FAILED(rv))
     return rv;
   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
 
   if (!mChannel) {
     // When we're a clone, the decoder might ask us to Open even though
     // we haven't established an mChannel (because we might not need one)
@@ -714,28 +730,28 @@ MediaResource* ChannelMediaResource::Clo
     // we don't have a channel. If the cache needs to read data from the clone
     // it will call CacheClientResume (or CacheClientSeek with aResume true)
     // which will recreate the channel. This way, if all of the media data
     // is already in the cache we don't create an unneccesary HTTP channel
     // and perform a useless HTTP transaction.
     resource->mSuspendCount = 1;
     resource->mCacheStream.InitAsClone(&mCacheStream);
     resource->mChannelStatistics = mChannelStatistics;
-    resource->mChannelStatistics.Stop(TimeStamp::Now());
+    resource->mChannelStatistics->Stop();
   }
   return resource;
 }
 
 void ChannelMediaResource::CloseChannel()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   {
     MutexAutoLock lock(mLock);
-    mChannelStatistics.Stop(TimeStamp::Now());
+    mChannelStatistics->Stop();
   }
 
   if (mListener) {
     mListener->Revoke();
     mListener = nullptr;
   }
 
   if (mChannel) {
@@ -827,17 +843,17 @@ void ChannelMediaResource::Suspend(bool 
     if (aCloseImmediately && mCacheStream.IsTransportSeekable()) {
       // Kill off our channel right now, but don't tell anyone about it.
       mIgnoreClose = true;
       CloseChannel();
       element->DownloadSuspended();
     } else if (mSuspendCount == 0) {
       {
         MutexAutoLock lock(mLock);
-        mChannelStatistics.Stop(TimeStamp::Now());
+        mChannelStatistics->Stop();
       }
       PossiblySuspend();
       element->DownloadSuspended();
     }
   }
 
   ++mSuspendCount;
 }
@@ -860,17 +876,17 @@ void ChannelMediaResource::Resume()
 
   NS_ASSERTION(mSuspendCount > 0, "Resume without previous Suspend!");
   --mSuspendCount;
   if (mSuspendCount == 0) {
     if (mChannel) {
       // Just wake up our existing channel
       {
         MutexAutoLock lock(mLock);
-        mChannelStatistics.Start(TimeStamp::Now());
+        mChannelStatistics->Start();
       }
       // if an error occurs after Resume, assume it's because the server
       // timed out the connection and we should reopen it.
       mReopenOnError = true;
       PossiblyResume();
       element->DownloadResumed();
     } else {
       int64_t totalLength = mCacheStream.GetLength();
@@ -1202,17 +1218,17 @@ ChannelMediaResource::Unpin()
 {
   mCacheStream.Unpin();
 }
 
 double
 ChannelMediaResource::GetDownloadRate(bool* aIsReliable)
 {
   MutexAutoLock lock(mLock);
-  return mChannelStatistics.GetRate(TimeStamp::Now(), aIsReliable);
+  return mChannelStatistics->GetRate(aIsReliable);
 }
 
 int64_t
 ChannelMediaResource::GetLength()
 {
   return mCacheStream.GetLength();
 }
 
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -41,32 +41,35 @@ class MediaDecoder;
  * channel is active.
  * 
  * All methods take "now" as a parameter so the user of this class can
  * control the timeline used.
  */
 class MediaChannelStatistics {
 public:
   MediaChannelStatistics() { Reset(); }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaChannelStatistics)
+
   void Reset() {
     mLastStartTime = TimeStamp();
     mAccumulatedTime = TimeDuration(0);
     mAccumulatedBytes = 0;
     mIsStarted = false;
   }
-  void Start(TimeStamp aNow) {
+  void Start() {
     if (mIsStarted)
       return;
-    mLastStartTime = aNow;
+    mLastStartTime = TimeStamp::Now();
     mIsStarted = true;
   }
-  void Stop(TimeStamp aNow) {
+  void Stop() {
     if (!mIsStarted)
       return;
-    mAccumulatedTime += aNow - mLastStartTime;
+    mAccumulatedTime += TimeStamp::Now() - mLastStartTime;
     mIsStarted = false;
   }
   void AddBytes(int64_t aBytes) {
     if (!mIsStarted) {
       // ignore this data, it may be related to seeking or some other
       // operation we don't care about
       return;
     }
@@ -74,59 +77,97 @@ public:
   }
   double GetRateAtLastStop(bool* aReliable) {
     double seconds = mAccumulatedTime.ToSeconds();
     *aReliable = seconds >= 1.0;
     if (seconds <= 0.0)
       return 0.0;
     return static_cast<double>(mAccumulatedBytes)/seconds;
   }
-  double GetRate(TimeStamp aNow, bool* aReliable) {
+  double GetRate(bool* aReliable) {
     TimeDuration time = mAccumulatedTime;
     if (mIsStarted) {
-      time += aNow - mLastStartTime;
+      time += TimeStamp::Now() - mLastStartTime;
     }
     double seconds = time.ToSeconds();
     *aReliable = seconds >= 3.0;
     if (seconds <= 0.0)
       return 0.0;
     return static_cast<double>(mAccumulatedBytes)/seconds;
   }
 private:
   int64_t      mAccumulatedBytes;
   TimeDuration mAccumulatedTime;
   TimeStamp    mLastStartTime;
   bool         mIsStarted;
 };
 
+// Forward declaration for use in MediaByteRange.
+class TimestampedMediaByteRange;
+
 // Represents a section of contiguous media, with a start and end offset.
 // Used to denote ranges of data which are cached.
 class MediaByteRange {
 public:
   MediaByteRange() : mStart(0), mEnd(0) {}
 
   MediaByteRange(int64_t aStart, int64_t aEnd)
     : mStart(aStart), mEnd(aEnd)
   {
     NS_ASSERTION(mStart < mEnd, "Range should end after start!");
   }
 
+  MediaByteRange(TimestampedMediaByteRange& aByteRange);
+
   bool IsNull() const {
     return mStart == 0 && mEnd == 0;
   }
 
   // Clears byte range values.
   void Clear() {
     mStart = 0;
     mEnd = 0;
   }
 
   int64_t mStart, mEnd;
 };
 
+// Represents a section of contiguous media, with a start and end offset, and
+// a timestamp representing the start time.
+class TimestampedMediaByteRange : public MediaByteRange {
+public:
+  TimestampedMediaByteRange() : MediaByteRange(), mStartTime(-1) {}
+
+  TimestampedMediaByteRange(int64_t aStart, int64_t aEnd, int64_t aStartTime)
+    : MediaByteRange(aStart, aEnd), mStartTime(aStartTime)
+  {
+    NS_ASSERTION(aStartTime >= 0, "Start time should not be negative!");
+  }
+
+  bool IsNull() const {
+    return MediaByteRange::IsNull() && mStartTime == -1;
+  }
+
+  // Clears byte range values.
+  void Clear() {
+    MediaByteRange::Clear();
+    mStartTime = -1;
+  }
+
+  // In usecs.
+  int64_t mStartTime;
+};
+
+inline MediaByteRange::MediaByteRange(TimestampedMediaByteRange& aByteRange)
+  : mStart(aByteRange.mStart), mEnd(aByteRange.mEnd)
+{
+  NS_ASSERTION(mStart < mEnd, "Range should end after start!");
+}
+
+
 /**
  * Provides a thread-safe, seek/read interface to resources
  * loaded from a URI. Uses MediaCache to cache data received over
  * Necko's async channel API, thus resolving the mismatch between clients
  * that need efficient random access to the data and protocols that do not
  * support efficient random access, such as HTTP.
  *
  * Instances of this class must be created on the main thread.
@@ -170,16 +211,18 @@ public:
   // because the underlying connection has been lost, or this resource
   // just can't be safely cloned). If this returns true, CloneData could
   // still fail. If this returns false, CloneData should not be called.
   virtual bool CanClone() { return false; }
   // Create a new stream of the same type that refers to the same URI
   // with a new channel. Any cached data associated with the original
   // stream should be accessible in the new stream too.
   virtual MediaResource* CloneData(MediaDecoder* aDecoder) = 0;
+  // Set statistics to be recorded to the object passed in.
+  virtual void RecordStatisticsTo(MediaChannelStatistics *aStatistics) { }
 
   // These methods are called off the main thread.
   // The mode is initially MODE_PLAYBACK.
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
   // This is the client's estimate of the playback rate assuming
   // the media plays continuously. The cache can't guess this itself
   // because it doesn't know when the decoder was paused, buffering, etc.
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) = 0;
@@ -294,16 +337,22 @@ public:
    */
   virtual nsresult OpenByteRange(nsIStreamListener** aStreamListener,
                                  MediaByteRange const &aByteRange)
   {
     return Open(aStreamListener);
   }
 
   /**
+   * Cancels current byte range requests previously opened via
+   * OpenByteRange.
+   */
+  virtual void CancelByteRangeOpen() { }
+
+  /**
    * Fills aRanges with MediaByteRanges representing the data which is cached
    * in the media cache. Stream should be pinned during call and while
    * aRanges is being used.
    */
   virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) = 0;
 
   // Ensure that the media cache writes any data held in its partial block.
   // Called on the main thread only.
@@ -401,24 +450,34 @@ public:
 
   // Notify that the last data byte range was loaded.
   virtual void NotifyLastByteRange() MOZ_OVERRIDE;
 
   // Main thread
   virtual nsresult Open(nsIStreamListener** aStreamListener);
   virtual nsresult OpenByteRange(nsIStreamListener** aStreamListener,
                                  MediaByteRange const & aByteRange);
+  virtual void     CancelByteRangeOpen();
   virtual nsresult Close();
   virtual void     Suspend(bool aCloseImmediately);
   virtual void     Resume();
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
   // Return true if the stream has been closed.
   bool IsClosed() const { return mCacheStream.IsClosed(); }
   virtual bool     CanClone();
   virtual MediaResource* CloneData(MediaDecoder* aDecoder);
+  // Set statistics to be recorded to the object passed in. If not called,
+  // |ChannelMediaResource| will create it's own statistics objects in |Open|.
+  void RecordStatisticsTo(MediaChannelStatistics *aStatistics) MOZ_OVERRIDE {
+    NS_ASSERTION(aStatistics, "Statistics param cannot be null!");
+    MutexAutoLock lock(mLock);
+    if (!mChannelStatistics) {
+      mChannelStatistics = aStatistics;
+    }
+  }
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
   virtual void     EnsureCacheUpToDate();
 
   // Other thread
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode);
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond);
   virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset);
@@ -438,16 +497,17 @@ public:
   virtual bool    IsSuspended();
 
   class Listener MOZ_FINAL : public nsIStreamListener,
                              public nsIInterfaceRequestor,
                              public nsIChannelEventSink
   {
   public:
     Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
+    ~Listener() {}
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIINTERFACEREQUESTOR
 
     void Revoke() { mResource = nullptr; }
@@ -516,17 +576,17 @@ protected:
   // channel.
   bool               mIgnoreClose;
 
   // Any thread access
   MediaCacheStream mCacheStream;
 
   // This lock protects mChannelStatistics
   Mutex               mLock;
-  MediaChannelStatistics mChannelStatistics;
+  nsRefPtr<MediaChannelStatistics> mChannelStatistics;
 
   // True if we couldn't suspend the stream and we therefore don't want
   // to resume later. This is usually due to the channel not being in the
   // isPending state at the time of the suspend request.
   bool mIgnoreResume;
 
   // True if we are seeking to get the real duration of the file.
   bool mSeekingForMetadata;
--- a/content/media/dash/DASHDecoder.cpp
+++ b/content/media/dash/DASHDecoder.cpp
@@ -150,19 +150,23 @@ DASHDecoder::DASHDecoder() :
   mPrincipal(nullptr),
   mDASHReader(nullptr),
   mVideoAdaptSetIdx(-1),
   mAudioRepDecoderIdx(-1),
   mVideoRepDecoderIdx(-1),
   mAudioSubsegmentIdx(0),
   mVideoSubsegmentIdx(0),
   mAudioMetadataReadCount(0),
-  mVideoMetadataReadCount(0)
+  mVideoMetadataReadCount(0),
+  mSeeking(false),
+  mStatisticsLock("DASHDecoder.mStatisticsLock")
 {
   MOZ_COUNT_CTOR(DASHDecoder);
+  mAudioStatistics = new MediaChannelStatistics();
+  mVideoStatistics = new MediaChannelStatistics();
 }
 
 DASHDecoder::~DASHDecoder()
 {
   MOZ_COUNT_DTOR(DASHDecoder);
 }
 
 MediaDecoderStateMachine*
@@ -186,18 +190,18 @@ DASHDecoder::ReleaseStateMachine()
   }
   for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
     mVideoRepDecoders[i]->ReleaseStateMachine();
   }
 }
 
 nsresult
 DASHDecoder::Load(MediaResource* aResource,
-                    nsIStreamListener** aStreamListener,
-                    MediaDecoder* aCloneDonor)
+                  nsIStreamListener** aStreamListener,
+                  MediaDecoder* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   mDASHReader = new DASHReader(this);
 
   nsresult rv = OpenResource(aResource, aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -530,16 +534,17 @@ DASHDecoder::CreateAudioSubResource(nsIU
   nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Create resource for representation.
   MediaResource* audioResource
     = MediaResource::Create(aAudioDecoder, channel);
   NS_ENSURE_TRUE(audioResource, nullptr);
 
+  audioResource->RecordStatisticsTo(mAudioStatistics);
   return audioResource;
 }
 
 MediaResource*
 DASHDecoder::CreateVideoSubResource(nsIURI* aUrl,
                                     MediaDecoder* aVideoDecoder)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@@ -551,16 +556,17 @@ DASHDecoder::CreateVideoSubResource(nsIU
   nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Create resource for representation.
   MediaResource* videoResource
     = MediaResource::Create(aVideoDecoder, channel);
   NS_ENSURE_TRUE(videoResource, nullptr);
 
+  videoResource->RecordStatisticsTo(mVideoStatistics);
   return videoResource;
 }
 
 nsresult
 DASHDecoder::CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ENSURE_ARG(aUrl);
@@ -904,42 +910,58 @@ DASHDecoder::PossiblySwitchDecoder(DASHR
   NS_ASSERTION((uint32_t)mVideoRepDecoderIdx < mVideoRepDecoders.Length(),
                "Index for video decoder is out of bounds!");
   NS_ASSERTION((uint32_t)mVideoSubsegmentIdx < VideoRepDecoder()->GetNumDataByteRanges(),
                "Can't switch to a byte range out of bounds.");
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   // Now, determine if and which decoder to switch to.
   // XXX This download rate is averaged over time, and only refers to the bytes
-  // downloaded for the given decoder. A running average would be better, and
+  // downloaded for the video decoder. A running average would be better, and
   // something that includes all downloads. But this will do for now.
   NS_ASSERTION(VideoRepDecoder(), "Video decoder should not be null.");
   NS_ASSERTION(VideoRepDecoder()->GetResource(),
                "Video resource should not be null");
   bool reliable = false;
-  double downloadRate = VideoRepDecoder()->GetResource()->GetDownloadRate(&reliable);
+  double downloadRate = 0;
+  {
+    MutexAutoLock lock(mStatisticsLock);
+    downloadRate = mVideoStatistics->GetRate(&reliable);
+  }
   uint32_t bestRepIdx = UINT32_MAX;
   bool noRepAvailable = !mMPDManager->GetBestRepForBandwidth(mVideoAdaptSetIdx,
                                                              downloadRate,
                                                              bestRepIdx);
-  LOG("downloadRate [%f] reliable [%s] bestRepIdx [%d] noRepAvailable",
-      downloadRate, (reliable ? "yes" : "no"), bestRepIdx,
+  LOG("downloadRate [%0.2f kbps] reliable [%s] bestRepIdx [%d] noRepAvailable [%s]",
+      downloadRate/1000.0, (reliable ? "yes" : "no"), bestRepIdx,
       (noRepAvailable ? "yes" : "no"));
 
   // If there is a higher bitrate stream that can be downloaded with the
   // current estimated bandwidth, step up to the next stream, for a graceful
   // increase in quality.
   uint32_t toDecoderIdx = mVideoRepDecoderIdx;
   if (bestRepIdx > toDecoderIdx) {
     toDecoderIdx = NS_MIN(toDecoderIdx+1, mVideoRepDecoders.Length()-1);
   } else if (toDecoderIdx < bestRepIdx) {
     // If the bitrate is too much for the current bandwidth, just use that
     // stream directly.
     toDecoderIdx = bestRepIdx;
   }
+
+  // Upgrade |toDecoderIdx| if a better subsegment was previously downloaded and
+  // is still cached.
+  if (mVideoSubsegmentIdx < mVideoSubsegmentLoads.Length() &&
+      toDecoderIdx < mVideoSubsegmentLoads[mVideoSubsegmentIdx]) {
+    // Check if the subsegment is cached.
+    uint32_t betterRepIdx = mVideoSubsegmentLoads[mVideoSubsegmentIdx];
+    if (mVideoRepDecoders[betterRepIdx]->IsSubsegmentCached(mVideoSubsegmentIdx)) {
+      toDecoderIdx = betterRepIdx;
+    }
+  }
+
   NS_ENSURE_TRUE(toDecoderIdx < mVideoRepDecoders.Length(),
                  NS_ERROR_ILLEGAL_VALUE);
 
   // Notify reader and sub decoders and do the switch.
   if (toDecoderIdx != mVideoRepDecoderIdx) {
     LOG("*** Switching video decoder from [%d] [%p] to [%d] [%p] at "
         "subsegment [%d]", mVideoRepDecoderIdx, VideoRepDecoder(),
         toDecoderIdx, mVideoRepDecoders[toDecoderIdx].get(),
@@ -953,16 +975,105 @@ DASHDecoder::PossiblySwitchDecoder(DASHR
     mVideoRepDecoders[mVideoRepDecoderIdx]->PrepareForSwitch();
     // Switch video decoders - equates to switching download source.
     mVideoRepDecoderIdx = toDecoderIdx;
   }
 
   return NS_OK;
 }
 
+nsresult
+DASHDecoder::Seek(double aTime)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_FALSE(mShuttingDown, NS_ERROR_UNEXPECTED);
+
+  LOG("Seeking to [%.2fs]", aTime);
+
+  {
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    // We want to stop the current series of downloads and restart later with
+    // the appropriate subsegment.
+
+    // 1 - Set the seeking flag, so that when current subsegments download (if
+    // any), the next subsegment will not be downloaded.
+    mSeeking = true;
+
+    // 2 - Cancel all current downloads to reset for seeking.
+    for (uint32_t i = 0; i < mAudioRepDecoders.Length(); i++) {
+      mAudioRepDecoders[i]->CancelByteRangeLoad();
+    }
+    for (uint32_t i = 0; i < mVideoRepDecoders.Length(); i++) {
+      mVideoRepDecoders[i]->CancelByteRangeLoad();
+    }
+  }
+
+  return MediaDecoder::Seek(aTime);
+}
+
+void
+DASHDecoder::NotifySeekInVideoSubsegment(int32_t aRepDecoderIdx,
+                                         int32_t aSubsegmentIdx)
+{
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
+
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  NS_ASSERTION(0 <= aRepDecoderIdx &&
+               aRepDecoderIdx < mVideoRepDecoders.Length(),
+               "Video decoder index is out of bounds");
+
+  // Reset current subsegment to match the one being seeked.
+  mVideoSubsegmentIdx = aSubsegmentIdx;
+  // Reset current decoder to match the one returned by
+  // |GetRepIdxForVideoSubsegmentLoad|.
+  mVideoRepDecoderIdx = aRepDecoderIdx;
+
+  mSeeking = false;
+
+  LOG("Dispatching load for video decoder [%d] [%p]: seek in subsegment [%d]",
+      mVideoRepDecoderIdx, VideoRepDecoder(), aSubsegmentIdx);
+
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(VideoRepDecoder(),
+                         &DASHRepDecoder::LoadNextByteRange);
+  nsresult rv = NS_DispatchToMainThread(event);
+  if (NS_FAILED(rv)) {
+    LOG("Error dispatching video byte range load: rv[0x%x].",
+        rv);
+    NetworkError();
+    return;
+  }
+}
+
+void
+DASHDecoder::NotifySeekInAudioSubsegment(int32_t aSubsegmentIdx)
+{
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
+
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  // Reset current subsegment to match the one being seeked.
+  mAudioSubsegmentIdx = aSubsegmentIdx;
+
+  LOG("Dispatching seeking load for audio decoder [%d] [%p]: subsegment [%d]",
+     mAudioRepDecoderIdx, AudioRepDecoder(), aSubsegmentIdx);
+
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(AudioRepDecoder(),
+                         &DASHRepDecoder::LoadNextByteRange);
+  nsresult rv = NS_DispatchToMainThread(event);
+  if (NS_FAILED(rv)) {
+    LOG("Error dispatching audio byte range load: rv[0x%x].",
+        rv);
+    NetworkError();
+    return;
+  }
+}
+
 bool
 DASHDecoder::IsDecoderAllowedToDownloadData(DASHRepDecoder* aRepDecoder)
 {
   NS_ASSERTION(aRepDecoder, "DASHRepDecoder pointer is null.");
 
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   // Only return true if |aRepDecoder| is active and metadata for all
   // representations has been downloaded.
@@ -973,26 +1084,168 @@ DASHDecoder::IsDecoderAllowedToDownloadD
 bool
 DASHDecoder::IsDecoderAllowedToDownloadSubsegment(DASHRepDecoder* aRepDecoder,
                                                   int32_t const aSubsegmentIdx)
 {
   NS_ASSERTION(aRepDecoder, "DASHRepDecoder pointer is null.");
 
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
+  // Forbid any downloads until we've been told what subsegment to seek to.
+  if (mSeeking) {
+    return false;
+  }
   // Return false if there is still metadata to be downloaded.
   if (mAudioMetadataReadCount != 0 || mVideoMetadataReadCount != 0) {
     return false;
   }
   // No audio switching; allow the audio decoder to download all subsegments.
   if (aRepDecoder == AudioRepDecoder()) {
     return true;
   }
 
   int32_t videoDecoderIdx = GetRepIdxForVideoSubsegmentLoad(aSubsegmentIdx);
   if (aRepDecoder == mVideoRepDecoders[videoDecoderIdx]) {
     return true;
   }
   return false;
 }
 
+void
+DASHDecoder::SetSubsegmentIndex(DASHRepDecoder* aRepDecoder,
+                                int32_t aSubsegmentIdx)
+{
+  NS_ASSERTION(0 <= aSubsegmentIdx,
+               "Subsegment index should not be negative!");
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  if (aRepDecoder == AudioRepDecoder()) {
+    mAudioSubsegmentIdx = aSubsegmentIdx;
+  } else if (aRepDecoder == VideoRepDecoder()) {
+    // If this is called in the context of a Seek, we need to cancel downloads
+    // from other rep decoders, or all rep decoders if we're not seeking in the
+    // current subsegment.
+    // Note: NotifySeekInSubsegment called from DASHReader will already have
+    // set the current decoder.
+    mVideoSubsegmentIdx = aSubsegmentIdx;
+  }
+}
+
+double
+DASHDecoder::ComputePlaybackRate(bool* aReliable)
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
+  NS_ASSERTION(aReliable, "Bool pointer aRelible should not be null!");
+
+  // While downloading the MPD, return 0; do not count manifest as media data.
+  if (mResource && !mMPDManager) {
+    return 0;
+  }
+
+  // Once MPD is downloaded, use the rate from the video decoder.
+  // XXX Not ideal, but since playback rate is used to estimate if we have
+  // enough data to continue playing, this should be sufficient.
+  double videoRate = 0;
+  if (VideoRepDecoder()) {
+    videoRate = VideoRepDecoder()->ComputePlaybackRate(aReliable);
+  }
+  return videoRate;
+}
+
+void
+DASHDecoder::UpdatePlaybackRate()
+{
+  MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  // While downloading the MPD, return silently; playback rate has no meaning
+  // for the manifest.
+  if (mResource && !mMPDManager) {
+    return;
+  }
+  // Once MPD is downloaded and audio/video decoder(s) are loading, forward to
+  // active rep decoders.
+  if (AudioRepDecoder()) {
+    AudioRepDecoder()->UpdatePlaybackRate();
+  }
+  if (VideoRepDecoder()) {
+    VideoRepDecoder()->UpdatePlaybackRate();
+  }
+}
+
+void
+DASHDecoder::NotifyPlaybackStarted()
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  // While downloading the MPD, return silently; playback rate has no meaning
+  // for the manifest.
+  if (mResource && !mMPDManager) {
+    return;
+  }
+  // Once MPD is downloaded and audio/video decoder(s) are loading, forward to
+  // active rep decoders.
+  if (AudioRepDecoder()) {
+    AudioRepDecoder()->NotifyPlaybackStarted();
+  }
+  if (VideoRepDecoder()) {
+    VideoRepDecoder()->NotifyPlaybackStarted();
+  }
+}
+
+void
+DASHDecoder::NotifyPlaybackStopped()
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  // While downloading the MPD, return silently; playback rate has no meaning
+  // for the manifest.
+  if (mResource && !mMPDManager) {
+    return;
+  }
+  // Once  // Once MPD is downloaded and audio/video decoder(s) are loading, forward to
+  // active rep decoders.
+  if (AudioRepDecoder()) {
+    AudioRepDecoder()->NotifyPlaybackStopped();
+  }
+  if (VideoRepDecoder()) {
+    VideoRepDecoder()->NotifyPlaybackStopped();
+  }
+}
+
+MediaDecoder::Statistics
+DASHDecoder::GetStatistics()
+{
+  MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
+  Statistics result;
+
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  if (mResource && !mMPDManager) {
+    return MediaDecoder::GetStatistics();
+  }
+
+  // XXX Use video decoder and its media resource to get stats.
+  // This assumes that the following getter functions are getting relevant
+  // video data only.
+  if (VideoRepDecoder() && VideoRepDecoder()->GetResource()) {
+    MediaResource *resource = VideoRepDecoder()->GetResource();
+    // Note: this rate reflects the rate observed for all video downloads.
+    result.mDownloadRate =
+      resource->GetDownloadRate(&result.mDownloadRateReliable);
+    result.mDownloadPosition =
+      resource->GetCachedDataEnd(VideoRepDecoder()->mDecoderPosition);
+    result.mTotalBytes = resource->GetLength();
+    result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
+    result.mDecoderPosition = VideoRepDecoder()->mDecoderPosition;
+    result.mPlaybackPosition = VideoRepDecoder()->mPlaybackPosition;
+  }
+  else {
+    result.mDownloadRate = 0;
+    result.mDownloadRateReliable = true;
+    result.mPlaybackRate = 0;
+    result.mPlaybackRateReliable = true;
+    result.mDecoderPosition = 0;
+    result.mPlaybackPosition = 0;
+    result.mDownloadPosition = 0;
+    result.mTotalBytes = 0;
+  }
+
+  return result;
+}
+
 } // namespace mozilla
-
--- a/content/media/dash/DASHDecoder.h
+++ b/content/media/dash/DASHDecoder.h
@@ -56,27 +56,37 @@ public:
   nsresult Load(MediaResource* aResource,
                 nsIStreamListener** aListener,
                 MediaDecoder* aCloneDonor);
 
   // Notifies download of MPD file has ended.
   // Called on the main thread only.
   void NotifyDownloadEnded(nsresult aStatus);
 
+  // Notification from |DASHReader| that a seek has occurred in
+  // |aSubsegmentIdx|. Passes notification onto subdecoder which downloaded
+  // the subsegment already, if download is in the past. Otherwise, it returns.
+  void NotifySeekInVideoSubsegment(int32_t aRepDecoderIdx,
+                                   int32_t aSubsegmentIdx);
+  void NotifySeekInAudioSubsegment(int32_t aSubsegmentIdx);
+
   // Notifies that a byte range download has ended. As per the DASH spec, this
   // allows for stream switching at the boundaries of the byte ranges.
   // Called on the main thread only.
   void NotifyDownloadEnded(DASHRepDecoder* aRepDecoder,
                            nsresult aStatus,
                            int32_t const aSubsegmentIdx);
 
   // Notification from an |MediaDecoderReader| class that metadata has been
   // read. Declared here to allow overloading.
   void OnReadMetadataCompleted() MOZ_OVERRIDE { }
 
+  // Seeks to aTime in seconds
+  nsresult Seek(double aTime) MOZ_OVERRIDE;
+
   // Notification from |DASHRepDecoder| that a metadata has been read.
   // |DASHDecoder| will initiate load of data bytes for active audio/video
   // decoders. Called on the decode thread.
   void OnReadMetadataCompleted(DASHRepDecoder* aRepDecoder);
 
   // Refers to downloading data bytes, i.e. non metadata.
   // Returns true if |aRepDecoder| is an active audio or video sub decoder AND
   // if metadata for all audio or video decoders has been read.
@@ -97,25 +107,17 @@ public:
   // Called on the main thread.
   nsresult PossiblySwitchDecoder(DASHRepDecoder* aRepDecoder);
 
   // Sets the byte range index for audio|video downloads. Will only increment
   // for current active decoders. Could be called from any thread.
   // Requires monitor because of write to |mAudioSubsegmentIdx| or
   // |mVideoSubsegmentIdx|.
   void SetSubsegmentIndex(DASHRepDecoder* aRepDecoder,
-                          uint32_t aSubsegmentIdx)
-  {
-    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-    if (aRepDecoder == AudioRepDecoder()) {
-      mAudioSubsegmentIdx = aSubsegmentIdx;
-    } else if (aRepDecoder == VideoRepDecoder()) {
-      mVideoSubsegmentIdx = aSubsegmentIdx;
-    }
-  }
+                          int32_t aSubsegmentIdx);
 private:
   // Increments the byte range index for audio|video downloads. Will only
   // increment for current active decoders. Could be called from any thread.
   // Requires monitor because of write to |mAudioSubsegmentIdx| or
   // |mVideoSubsegmentIdx|.
   void IncrementSubsegmentIndex(DASHRepDecoder* aRepDecoder)
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
@@ -140,27 +142,73 @@ public:
     }
     return (-1);
   }
 
   // Returns the index of the rep decoder used to load a subsegment. Will enter
   // monitor for read access off the decode thread.
   int32_t GetRepIdxForVideoSubsegmentLoad(int32_t aSubsegmentIdx)
   {
-    NS_ASSERTION(0 < aSubsegmentIdx, "Subsegment index should not be negative.");
+    NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
     ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
                                            GetReentrantMonitor());
     if ((uint32_t)aSubsegmentIdx < mVideoSubsegmentLoads.Length()) {
       return mVideoSubsegmentLoads[aSubsegmentIdx];
     } else {
       // If it hasn't been downloaded yet, use the lowest bitrate decoder.
       return 0;
     }
   }
 
+  int32_t GetSwitchCountAtVideoSubsegment(int32_t aSubsegmentIdx)
+  {
+    ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
+                                           GetReentrantMonitor());
+    NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
+    if (aSubsegmentIdx == 0) {
+      // Do the zeroeth switch next.
+      return 0;
+    }
+    int32_t switchCount = 0;
+    for (uint32_t i = 1;
+         i < mVideoSubsegmentLoads.Length() &&
+         i <= (uint32_t)aSubsegmentIdx;
+         i++) {
+      if (mVideoSubsegmentLoads[i-1] != mVideoSubsegmentLoads[i]) {
+        switchCount++;
+      }
+    }
+    return switchCount;
+  }
+
+  // The actual playback rate computation. The monitor must be held.
+  // XXX Computes playback for the current video rep decoder only.
+  double ComputePlaybackRate(bool* aReliable) MOZ_OVERRIDE;
+
+  // Something has changed that could affect the computed playback rate,
+  // so recompute it. The monitor must be held. Will be forwarded to current
+  // audio and video rep decoders.
+  void UpdatePlaybackRate() MOZ_OVERRIDE;
+
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity starting on the channel. The monitor must be held.
+  virtual void NotifyPlaybackStarted() MOZ_OVERRIDE;
+
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity stopping on the channel. The monitor must be held.
+  virtual void NotifyPlaybackStopped() MOZ_OVERRIDE;
+
+  // Return statistics. This is used for progress events and other things.
+  // This can be called from any thread. It's only a snapshot of the
+  // current state, since other threads might be changing the state
+  // at any time.
+  // XXX Stats are calculated based on the current video rep decoder, with the
+  // exception of download rate, which is based on all video downloads.
+  virtual Statistics GetStatistics() MOZ_OVERRIDE;
+
   // Drop reference to state machine and tell sub-decoders to do the same.
   // Only called during shutdown dance, on main thread only.
   void ReleaseStateMachine();
 
   // Overridden to forward |Shutdown| to sub-decoders.
   // Called on the main thread only.
   void Shutdown();
 
@@ -287,13 +335,25 @@ private:
   // zero again, all metadata has been read for audio or video, and data bytes
   // can be downloaded.
   uint32_t mAudioMetadataReadCount;
   uint32_t mVideoMetadataReadCount;
 
   // Array records the index of the decoder/Representation which loaded each
   // subsegment.
   nsTArray<int32_t> mVideoSubsegmentLoads;
+
+  // True when Seek is called; will block any downloads until
+  // |NotifySeekInSubsegment| is called, which will set it to false, and will
+  // start a new series of downloads from the seeked subsegment.
+  bool mSeeking;
+
+  // Mutex for statistics.
+  Mutex mStatisticsLock;
+  // Stores snapshot statistics, such as download rate, for the audio|video
+  // data streams. |mStatisticsLock| must be locked for access.
+  nsRefPtr<MediaChannelStatistics> mAudioStatistics;
+  nsRefPtr<MediaChannelStatistics> mVideoStatistics;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/dash/DASHReader.cpp
+++ b/content/media/dash/DASHReader.cpp
@@ -52,16 +52,32 @@ DASHReader::DASHReader(AbstractMediaDeco
 }
 
 DASHReader::~DASHReader()
 {
   MOZ_COUNT_DTOR(DASHReader);
 }
 
 nsresult
+DASHReader::ResetDecode()
+{
+  MediaDecoderReader::ResetDecode();
+  nsresult rv;
+  for (uint i = 0; i < mAudioReaders.Length(); i++) {
+    rv = mAudioReaders[i]->ResetDecode();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  for (uint i = 0; i < mVideoReaders.Length(); i++) {
+    rv = mVideoReaders[i]->ResetDecode();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+nsresult
 DASHReader::Init(MediaDecoderReader* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   NS_ASSERTION(mAudioReaders.Length() != 0 && mVideoReaders.Length() != 0,
                "Audio and video readers should exist already.");
 
@@ -132,17 +148,17 @@ DASHReader::AudioQueueMemoryInUse()
 {
   ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
                                          mDecoder->GetReentrantMonitor());
   return AudioQueueMemoryInUse();
 }
 
 bool
 DASHReader::DecodeVideoFrame(bool &aKeyframeSkip,
-                               int64_t aTimeThreshold)
+                             int64_t aTimeThreshold)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   if (mVideoReader) {
    return mVideoReader->DecodeVideoFrame(aKeyframeSkip, aTimeThreshold);
   } else {
    return false;
   }
 }
@@ -202,38 +218,102 @@ DASHReader::ReadMetadata(VideoInfo* aInf
 
   *aInfo = mInfo;
 
   return NS_OK;
 }
 
 nsresult
 DASHReader::Seek(int64_t aTime,
-                   int64_t aStartTime,
-                   int64_t aEndTime,
-                   int64_t aCurrentTime)
+                 int64_t aStartTime,
+                 int64_t aEndTime,
+                 int64_t aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
+  NS_ENSURE_SUCCESS(ResetDecode(), NS_ERROR_FAILURE);
+
+  LOG("Seeking to [%.2fs]", aTime/1000000.0);
+
   nsresult rv;
+  DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
 
   if (mAudioReader) {
+    int64_t subsegmentIdx = -1;
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      subsegmentIdx = mAudioReader->GetSubsegmentForSeekTime(aTime);
+      NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
+    }
+    dashDecoder->NotifySeekInAudioSubsegment(subsegmentIdx);
+
     rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
     NS_ENSURE_SUCCESS(rv, rv);
   }
+
   if (mVideoReader) {
-    rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
-    NS_ENSURE_SUCCESS(rv, rv);
+    // Determine the video subsegment we're seeking to.
+    int32_t subsegmentIdx = -1;
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      subsegmentIdx = mVideoReader->GetSubsegmentForSeekTime(aTime);
+      NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
+    }
+
+    LOG("Seek to [%.2fs] found in video subsegment [%d]",
+        aTime/1000000.0, subsegmentIdx);
+
+    // Determine if/which video reader previously downloaded this subsegment.
+    int32_t readerIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(subsegmentIdx);
+
+    dashDecoder->NotifySeekInVideoSubsegment(readerIdx, subsegmentIdx);
+
+    if (0 <= readerIdx) {
+      NS_ENSURE_TRUE(readerIdx < mVideoReaders.Length(),
+                     NS_ERROR_ILLEGAL_VALUE);
+      // Switch to this reader and do the Seek.
+      DASHRepReader* fromReader = mVideoReader;
+      DASHRepReader* toReader = mVideoReaders[readerIdx];
+
+      {
+        ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+        if (fromReader != toReader) {
+          LOG("Switching video readers now from [%p] to [%p] for a seek to "
+              "[%.2fs] in subsegment [%d]",
+              fromReader, toReader, aTime/1000000.0, subsegmentIdx);
+
+          mVideoReader = toReader;
+        }
+      }
+
+      rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+      if (NS_FAILED(rv)) {
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Go back to the appropriate count in the switching history, and setup
+      // this main reader and the sub readers for the next switch (if any).
+      {
+        ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+        mSwitchCount = dashDecoder->GetSwitchCountAtVideoSubsegment(subsegmentIdx);
+        LOG("After mVideoReader->Seek() mSwitchCount %d", mSwitchCount);
+        NS_ENSURE_TRUE(0 <= mSwitchCount, NS_ERROR_ILLEGAL_VALUE);
+        NS_ENSURE_TRUE(mSwitchCount <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
+      }
+    } else {
+      LOG("Error getting rep idx for video subsegment [%d]",
+          subsegmentIdx);
+    }
   }
   return NS_OK;
 }
 
 nsresult
 DASHReader::GetBuffered(nsTimeRanges* aBuffered,
-                          int64_t aStartTime)
+                        int64_t aStartTime)
 {
   NS_ENSURE_ARG(aBuffered);
 
   MediaResource* resource = nullptr;
   AbstractMediaDecoder* decoder = nullptr;
 
   // Need to find intersect of |nsTimeRanges| for audio and video.
   nsTimeRanges audioBuffered, videoBuffered;
@@ -364,18 +444,18 @@ DASHReader::VideoQueue()
   ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
                                          mDecoder->GetReentrantMonitor());
   NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
   return mVideoQueue;
 }
 
 void
 DASHReader::RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
-                                       uint32_t aToReaderIdx,
-                                       uint32_t aSubsegmentIdx)
+                                     uint32_t aToReaderIdx,
+                                     uint32_t aSubsegmentIdx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(aFromReaderIdx < mVideoReaders.Length(),
                "From index is greater than number of video readers!");
   NS_ASSERTION(aToReaderIdx < mVideoReaders.Length(),
                "To index is greater than number of video readers!");
   NS_ASSERTION(aToReaderIdx != aFromReaderIdx,
                "Don't request switches to same reader!");
@@ -388,16 +468,24 @@ DASHReader::RequestVideoReaderSwitch(uin
   DASHRepReader* fromReader = mVideoReaders[aFromReaderIdx];
   DASHRepReader* toReader = mVideoReaders[aToReaderIdx];
 
   LOG("Switch requested from reader [%d] [%p] to reader [%d] [%p] "
       "at subsegment[%d].",
       aFromReaderIdx, fromReader, aToReaderIdx, toReader, aSubsegmentIdx);
 
   // Append the subsegment index to the list of pending switches.
+  for (uint32_t i = 0; i < mSwitchToVideoSubsegmentIndexes.Length(); i++) {
+    if (mSwitchToVideoSubsegmentIndexes[i] == aSubsegmentIdx) {
+      // A backwards |Seek| has changed the switching history; delete from
+      // this point on.
+      mSwitchToVideoSubsegmentIndexes.TruncateLength(i);
+      break;
+    }
+  }
   mSwitchToVideoSubsegmentIndexes.AppendElement(aSubsegmentIdx);
 
   // Tell the SWITCH FROM reader when it should stop reading.
   fromReader->RequestSwitchAtSubsegment(aSubsegmentIdx, toReader);
 
   // Tell the SWITCH TO reader to seek to the correct offset.
   toReader->RequestSeekToSubsegment(aSubsegmentIdx);
 
--- a/content/media/dash/DASHReader.h
+++ b/content/media/dash/DASHReader.h
@@ -24,26 +24,27 @@ namespace mozilla {
 
 class DASHRepReader;
 
 class DASHReader : public MediaDecoderReader
 {
 public:
   DASHReader(AbstractMediaDecoder* aDecoder);
   ~DASHReader();
+  nsresult ResetDecode() MOZ_OVERRIDE;
 
   // Adds a pointer to a audio/video reader for a media |Representation|.
   // Called on the main thread only.
   void AddAudioReader(DASHRepReader* aAudioReader);
   void AddVideoReader(DASHRepReader* aVideoReader);
 
   // Waits for metadata bytes to be downloaded, then reads and parses them.
   // Called on the decode thread only.
   nsresult ReadMetadata(VideoInfo* aInfo,
-                        MetadataTags** aTags);
+                        MetadataTags** aTags) MOZ_OVERRIDE;
 
   // Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
   // notification, whichever comes first. Ensures no attempt to read metadata
   // during |DASHDecoder|::|Shutdown|. Called on decode thread only.
   nsresult WaitForMetadata() {
     NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
     ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
     while (true) {
@@ -74,51 +75,51 @@ public:
     ReentrantMonitorAutoEnter metadataMon(mReadMetadataMonitor);
     mDecoderIsShuttingDown = true;
     // Notify |ReadMetadata| of the shutdown if it's waiting.
     metadataMon.NotifyAll();
   }
 
   // Audio/video status are dependent on the presence of audio/video readers.
   // Call on decode thread only.
-  bool HasAudio();
-  bool HasVideo();
+  bool HasAudio() MOZ_OVERRIDE;
+  bool HasVideo() MOZ_OVERRIDE;
 
   // Returns references to the audio/video queues of sub-readers. Called on
   // decode, state machine and audio threads.
   MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE;
   MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE;
 
   // Called from MediaDecoderStateMachine on the main thread.
-  nsresult Init(MediaDecoderReader* aCloneDonor);
+  nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
 
   // Used by |MediaMemoryReporter|.
-  int64_t VideoQueueMemoryInUse();
-  int64_t AudioQueueMemoryInUse();
+  int64_t VideoQueueMemoryInUse() MOZ_OVERRIDE;
+  int64_t AudioQueueMemoryInUse() MOZ_OVERRIDE;
 
   // Called on the decode thread, at the start of the decode loop, before
   // |DecodeVideoFrame|.  Carries out video reader switch if previously
   // requested, and tells sub-readers to |PrepareToDecode|.
   void PrepareToDecode() MOZ_OVERRIDE;
 
   // Called on the decode thread.
-  bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold);
-  bool DecodeAudioData();
+  bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) MOZ_OVERRIDE;
+  bool DecodeAudioData() MOZ_OVERRIDE;
 
   // Converts seek time to byte offset. Called on the decode thread only.
   nsresult Seek(int64_t aTime,
                 int64_t aStartTime,
                 int64_t aEndTime,
-                int64_t aCurrentTime);
+                int64_t aCurrentTime) MOZ_OVERRIDE;
 
   // Called by state machine on multiple threads.
-  nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
+  nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
 
   // Called on the state machine or decode threads.
-  VideoData* FindStartTime(int64_t& aOutStartTime);
+  VideoData* FindStartTime(int64_t& aOutStartTime) MOZ_OVERRIDE;
 
   // Prepares for an upcoming switch of video readers. Called by
   // |DASHDecoder| when it has switched download streams. Sets the index of
   // the reader to switch TO and the index of the subsegment to switch AT
   // (start offset). (Note: Subsegment boundaries are switch access points for
   // DASH-WebM). Called on the main thread. Must be in the decode monitor.
   void RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
                                 uint32_t aToReaderIdx,
--- a/content/media/dash/DASHRepDecoder.cpp
+++ b/content/media/dash/DASHRepDecoder.cpp
@@ -68,18 +68,18 @@ void
 DASHRepDecoder::SetReader(WebMReader* aReader)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   mReader = aReader;
 }
 
 nsresult
 DASHRepDecoder::Load(MediaResource* aResource,
-                       nsIStreamListener** aListener,
-                       MediaDecoder* aCloneDonor)
+                     nsIStreamListener** aListener,
+                     MediaDecoder* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ENSURE_TRUE(mMPDRepresentation, NS_ERROR_NOT_INITIALIZED);
 
   // Get init range and index range from MPD.
   SegmentBase const * segmentBase = mMPDRepresentation->GetSegmentBase();
   NS_ENSURE_TRUE(segmentBase, NS_ERROR_NULL_POINTER);
 
@@ -126,16 +126,17 @@ DASHRepDecoder::NotifyDownloadEnded(nsre
   if (!mMainDecoder) {
     LOG("Error! Main Decoder is reported as null: mMainDecoder [%p]",
         mMainDecoder.get());
     DecodeError();
     return;
   }
 
   if (NS_SUCCEEDED(aStatus)) {
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     // Decrement counter as metadata chunks are downloaded.
     // Note: Reader gets next chunk download via |ChannelMediaResource|:|Seek|.
     if (mMetadataChunkCount > 0) {
       LOG("Metadata chunk [%d] downloaded: range requested [%lld - %lld] "
           "subsegmentIdx [%d]",
           mMetadataChunkCount,
           mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx);
       mMetadataChunkCount--;
@@ -143,19 +144,16 @@ DASHRepDecoder::NotifyDownloadEnded(nsre
       LOG("Byte range downloaded: status [%x] range requested [%lld - %lld] "
           "subsegmentIdx [%d]",
           aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd,
           mSubsegmentIdx);
       if ((uint32_t)mSubsegmentIdx == mByteRanges.Length()-1) {
         mResource->NotifyLastByteRange();
       }
       // Notify main decoder that a DATA byte range is downloaded.
-      // Only notify IF this decoder is allowed to download data.
-      NS_ASSERTION(mMainDecoder->IsDecoderAllowedToDownloadData(this),
-                   "This decoder should not have downloaded data.");
       mMainDecoder->NotifyDownloadEnded(this, aStatus, mSubsegmentIdx);
     }
   } else if (aStatus == NS_BINDING_ABORTED) {
     LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
     if (mMainDecoder) {
       mMainDecoder->LoadAborted();
     }
     return;
@@ -192,16 +190,17 @@ DASHRepDecoder::OnReadMetadataCompleted(
 nsresult
 DASHRepDecoder::PopulateByteRanges()
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
   // Should not be called during shutdown.
   NS_ENSURE_FALSE(mShuttingDown, NS_ERROR_UNEXPECTED);
 
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (!mByteRanges.IsEmpty()) {
     return NS_OK;
   }
   NS_ENSURE_TRUE(mReader, NS_ERROR_NULL_POINTER);
   LOG1("Populating byte range array.");
   return mReader->GetSubsegmentByteRanges(mByteRanges);
 }
 
@@ -212,16 +211,17 @@ DASHRepDecoder::LoadNextByteRange()
   NS_ASSERTION(mResource, "Error: resource is reported as null!");
 
   // Return silently if shutting down.
   if (mShuttingDown) {
     LOG1("Shutting down! Ignoring LoadNextByteRange().");
     return;
   }
 
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   NS_ASSERTION(mMainDecoder, "Error: main decoder is null!");
   NS_ASSERTION(mMainDecoder->IsDecoderAllowedToDownloadData(this),
                "Should not be called on non-active decoders!");
 
   // Cannot have empty byte ranges.
   if (mByteRanges.IsEmpty()) {
     LOG1("Error getting list of subsegment byte ranges.");
     DecodeError();
@@ -245,53 +245,104 @@ DASHRepDecoder::LoadNextByteRange()
   // Request a seek for the first reader. Required so that the reader is
   // primed to start here, and will block subsequent subsegment seeks unless
   // the subsegment has been read.
   if (subsegmentIdx == 0) {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mReader->RequestSeekToSubsegment(0);
   }
 
+  // Query resource for cached ranges; only download if it's not there.
+  if (IsSubsegmentCached(mSubsegmentIdx)) {
+    LOG("Subsegment [%d] bytes [%lld] to [%lld] already cached. No need to "
+        "download.", mSubsegmentIdx,
+        mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &DASHRepDecoder::DoNotifyDownloadEnded);
+    nsresult rv = NS_DispatchToMainThread(event);
+    if (NS_FAILED(rv)) {
+      LOG("Error notifying subsegment [%d] cached: rv[0x%x].",
+          mSubsegmentIdx, rv);
+      NetworkError();
+    }
+    return;
+  }
+
   // Open byte range corresponding to subsegment.
   nsresult rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
   if (NS_FAILED(rv)) {
     LOG("Error opening byte range [%lld - %lld]: subsegmentIdx [%d] rv [%x].",
         mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx, rv);
     NetworkError();
     return;
   }
 }
 
+void
+DASHRepDecoder::CancelByteRangeLoad()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ASSERTION(mResource, "Error: resource is reported as null!");
+
+  if (mCurrentByteRange.IsNull() || mSubsegmentIdx < 0) {
+    LOG1("Canceling current byte range load: none to cancel.");
+    return;
+  }
+  LOG("Canceling current byte range load: [%lld] to [%lld] subsegment "
+      "[%lld]", mCurrentByteRange.mStart, mCurrentByteRange.mEnd,
+      mSubsegmentIdx);
+
+  mResource->CancelByteRangeOpen();
+}
+
+bool
+DASHRepDecoder::IsSubsegmentCached(int32_t aSubsegmentIdx)
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  MediaByteRange byteRange = mByteRanges[aSubsegmentIdx];
+  int64_t start = mResource->GetNextCachedData(byteRange.mStart);
+  int64_t end = mResource->GetCachedDataEnd(byteRange.mStart);
+  return (start == byteRange.mStart &&
+          end >= byteRange.mEnd);
+}
+
+void
+DASHRepDecoder::DoNotifyDownloadEnded()
+{
+  NotifyDownloadEnded(NS_OK);
+}
+
 nsresult
 DASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
-                                      MediaByteRange& aByteRange)
+                                    MediaByteRange& aByteRange)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Only check data ranges if they're available and if this decoder is active,
   // i.e. inactive rep decoders should only load metadata.
-  bool canDownloadData = mMainDecoder->IsDecoderAllowedToDownloadData(this);
-  if (canDownloadData) {
-    for (int i = 0; i < mByteRanges.Length(); i++) {
-      NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
-      // Check if |aOffset| lies within the current data range.
-      if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  for (int i = 0; i < mByteRanges.Length(); i++) {
+    NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
+    // Check if |aOffset| lies within the current data range.
+    if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
+      if (mMainDecoder->IsDecoderAllowedToDownloadSubsegment(this, i)) {
         mCurrentByteRange = aByteRange = mByteRanges[i];
         mSubsegmentIdx = i;
         // XXX Hack: should be setting subsegment outside this function, but
         // need to review seeking for multiple switches anyhow.
         mMainDecoder->SetSubsegmentIndex(this, i);
         LOG("Getting DATA range [%d] for seek offset [%lld]: "
             "bytes [%lld] to [%lld]",
             i, aOffset, aByteRange.mStart, aByteRange.mEnd);
         return NS_OK;
       }
+      break;
     }
-  } else {
-    LOG1("Restricting seekable byte ranges to metadata for this decoder.");
   }
   // Don't allow metadata downloads once they're loaded and byte ranges have
   // been populated.
   bool canDownloadMetadata = mByteRanges.IsEmpty();
   if (canDownloadMetadata) {
     // Check metadata ranges; init range.
     if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
       mCurrentByteRange = aByteRange = mInitByteRange;
@@ -309,22 +360,21 @@ DASHRepDecoder::GetByteRangeForSeek(int6
       return NS_OK;
     }
   } else {
     LOG1("Metadata should be read; inhibiting further metadata downloads.");
   }
 
   // If no byte range is found by this stage, clear the parameter and return.
   aByteRange.Clear();
-  if (mByteRanges.IsEmpty() || !canDownloadData || !canDownloadMetadata) {
+  if (mByteRanges.IsEmpty() || !canDownloadMetadata) {
     // Assume mByteRanges will be populated after metadata is read.
-    LOG("Data ranges not populated [%s]; data download restricted [%s]; "
-        "metadata download restricted [%s]: offset[%lld].",
+    LOG("Data ranges not populated [%s]; metadata download restricted [%s]: "
+        "offset[%lld].",
         (mByteRanges.IsEmpty() ? "yes" : "no"),
-        (canDownloadData ? "no" : "yes"),
         (canDownloadMetadata ? "no" : "yes"), aOffset);
     return NS_ERROR_NOT_AVAILABLE;
   } else {
     // Cannot seek to an unknown offset.
     // XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
     LOG("Error! Offset [%lld] is in an unknown range!", aOffset);
     return NS_ERROR_ILLEGAL_VALUE;
   }
@@ -369,18 +419,18 @@ DASHRepDecoder::SetMediaSeekable(bool aM
 void
 DASHRepDecoder::Progress(bool aTimer)
 {
   if (mMainDecoder) { mMainDecoder->Progress(aTimer); }
 }
 
 void
 DASHRepDecoder::NotifyDataArrived(const char* aBuffer,
-                                    uint32_t aLength,
-                                    int64_t aOffset)
+                                  uint32_t aLength,
+                                  int64_t aOffset)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   LOG("Data bytes [%lld - %lld] arrived via buffer [%p].",
       aOffset, aOffset+aLength, aBuffer);
   // Notify reader directly, since call to |MediaDecoderStateMachine|::
   // |NotifyDataArrived| will go to |DASHReader|::|NotifyDataArrived|, which
   // has no way to forward the notification to the correct sub-reader.
@@ -417,24 +467,30 @@ bool
 DASHRepDecoder::OnDecodeThread() const
 {
   return (mMainDecoder ? mMainDecoder->OnDecodeThread() : false);
 }
 
 ReentrantMonitor&
 DASHRepDecoder::GetReentrantMonitor()
 {
-  return mMainDecoder->GetReentrantMonitor();
+  NS_ASSERTION(mMainDecoder, "Can't get monitor if main decoder is null!");
+  if (mMainDecoder) {
+    return mMainDecoder->GetReentrantMonitor();
+  } else {
+    // XXX If mMainDecoder is gone, most likely we're past shutdown and
+    // a waiting function has been wakened. Just return this decoder's own
+    // monitor and let the function complete.
+    return MediaDecoder::GetReentrantMonitor();
+  }
 }
 
 mozilla::layers::ImageContainer*
 DASHRepDecoder::GetImageContainer()
 {
-  NS_ASSERTION(mMainDecoder && mMainDecoder->OnDecodeThread(),
-               "Should be on decode thread.");
   return (mMainDecoder ? mMainDecoder->GetImageContainer() : nullptr);
 }
 
 void
 DASHRepDecoder::DecodeError()
 {
   if (NS_IsMainThread()) {
     MediaDecoder::DecodeError();
--- a/content/media/dash/DASHRepDecoder.h
+++ b/content/media/dash/DASHRepDecoder.h
@@ -76,16 +76,22 @@ public:
   nsresult Load(MediaResource* aResource = nullptr,
                 nsIStreamListener** aListener = nullptr,
                 MediaDecoder* aCloneDonor = nullptr);
 
   // Loads the next byte range (or first one on first call). Called on the main
   // thread only.
   void LoadNextByteRange();
 
+  // Cancels current byte range loads. Called on the main thread only.
+  void CancelByteRangeLoad();
+
+  // Returns true if the subsegment is already in the media cache.
+  bool IsSubsegmentCached(int32_t aSubsegmentIdx);
+
   // Calls from DASHRepDecoder. Called on the main thread only.
   void SetReader(WebMReader* aReader);
 
   // Called if the media file encounters a network error. Call on the main
   // thread only.
   void NetworkError();
 
   // Set the duration of the media resource in units of seconds.
@@ -114,22 +120,33 @@ public:
   // Called by MediaResource when some data has been received.
   // Call on the main thread only.
   void NotifyBytesDownloaded();
 
   // Notify that a byte range request has been completed by the media resource.
   // Called on the main thread only.
   void NotifyDownloadEnded(nsresult aStatus);
 
+  // Called asynchronously by |LoadNextByteRange| if the data is already in the
+  // media cache. This will call NotifyDownloadEnded on the main thread with
+  // |aStatus| of NS_OK.
+  void DoNotifyDownloadEnded();
+
   // Called by MediaResource when the "cache suspended" status changes.
   // If MediaResource::IsSuspendedByCache returns true, then the decoder
   // should stop buffering or otherwise waiting for download progress and
   // start consuming data, if possible, because the cache is full.
   void NotifySuspendedStatusChanged();
 
+  // Increments the parsed and decoded frame counters by the passed in counts.
+  // Can be called on any thread.
+  void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE {
+    if (mMainDecoder) {mMainDecoder->NotifyDecodedFrames(aParsed, aDecoded); }
+  }
+
   // Gets a byte range containing the byte offset. Call on main thread only.
   nsresult GetByteRangeForSeek(int64_t const aOffset,
                                MediaByteRange& aByteRange);
 
   // Gets the number of data byte ranges (not inc. metadata).
   uint32_t GetNumDataByteRanges() {
     return mByteRanges.Length();
   }
@@ -151,16 +168,19 @@ public:
 
   // Called when Metadata has been read; notifies that index data is read.
   // Called on the decode thread only.
   void OnReadMetadataCompleted() MOZ_OVERRIDE;
 
   // Overridden to cleanup ref to |DASHDecoder|. Called on main thread only.
   void Shutdown() {
     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+    // Remove ref to state machine before |MediaDecoder|::|Shutdown|, since
+    // |DASHDecoder| is responsible for its shutdown.
+    mDecoderStateMachine = nullptr;
     // Call parent class shutdown.
     MediaDecoder::Shutdown();
     NS_ENSURE_TRUE(mShuttingDown, );
     // Cleanup ref to main decoder.
     mMainDecoder = nullptr;
   }
 
   // Drop reference to state machine and mReader (owned by state machine).
--- a/content/media/dash/DASHRepReader.h
+++ b/content/media/dash/DASHRepReader.h
@@ -36,16 +36,19 @@ public:
   virtual void SetMainReader(DASHReader *aMainReader) = 0;
 
   // Sets range for initialization bytes; used by DASH.
   virtual void SetInitByteRange(MediaByteRange &aByteRange) = 0;
 
   // Sets range for index frame bytes; used by DASH.
   virtual void SetIndexByteRange(MediaByteRange &aByteRange) = 0;
 
+  // Returns the index of the subsegment which contains the seek time (usecs).
+  virtual int64_t GetSubsegmentForSeekTime(int64_t aSeekToTime) = 0;
+
   // Returns list of ranges for index frame start/end offsets. Used by DASH.
   virtual nsresult GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges) = 0;
 
   // Returns true if the reader has reached a DASH switch access point.
   virtual bool HasReachedSubsegment(uint32_t aSubsegmentIndex) = 0;
 
   // Requests a seek to the start of a particular DASH subsegment.
   virtual void RequestSeekToSubsegment(uint32_t aIdx) = 0;
--- a/content/media/webm/WebMReader.cpp
+++ b/content/media/webm/WebMReader.cpp
@@ -186,16 +186,26 @@ nsresult WebMReader::ResetDecode()
   // Ignore failed results from vorbis_synthesis_restart. They
   // aren't fatal and it fails when ResetDecode is called at a
   // time when no vorbis data has been read.
   vorbis_synthesis_restart(&mVorbisDsp);
 
   mVideoPackets.Reset();
   mAudioPackets.Reset();
 
+#ifdef MOZ_DASH
+  LOG(PR_LOG_DEBUG, ("Resetting DASH seek vars"));
+  mSwitchingCluster = -1;
+  mNextReader = nullptr;
+  mSeekToCluster = -1;
+  mCurrentOffset = -1;
+  mPushVideoPacketToNextReader = false;
+  mReachedSwitchAccessPoint = false;
+#endif
+
   return res;
 }
 
 void WebMReader::Cleanup()
 {
   if (mContext) {
     nestegg_destroy(mContext);
     mContext = nullptr;
@@ -376,45 +386,50 @@ nsresult WebMReader::ReadMetadata(VideoI
   if (!mCuesByteRange.IsNull()) {
     maxOffset = mCuesByteRange.mEnd;
 
     // Iterate through cluster ranges until nestegg returns the last one
     NS_ENSURE_TRUE(mClusterByteRanges.IsEmpty(),
                    NS_ERROR_ALREADY_INITIALIZED);
     int clusterNum = 0;
     bool done = false;
+    uint64_t timestamp;
     do {
       mClusterByteRanges.AppendElement();
       r = nestegg_get_cue_point(mContext, clusterNum, maxOffset,
                                 &(mClusterByteRanges[clusterNum].mStart),
-                                &(mClusterByteRanges[clusterNum].mEnd));
+                                &(mClusterByteRanges[clusterNum].mEnd),
+                                &timestamp);
       if (r != 0) {
         Cleanup();
         return NS_ERROR_FAILURE;
       }
       LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: Cluster [%d]: "
-                         "start [%d] end [%d]",
+                         "start [%lld] end [%lld], timestamp [%.2llfs]",
                          this, mDecoder, clusterNum,
                          mClusterByteRanges[clusterNum].mStart,
-                         mClusterByteRanges[clusterNum].mEnd));
+                         mClusterByteRanges[clusterNum].mEnd,
+                         timestamp/NS_PER_S));
+      mClusterByteRanges[clusterNum].mStartTime = timestamp/NS_PER_USEC;
       // Last cluster will have '-1' as end value
       if (mClusterByteRanges[clusterNum].mEnd == -1) {
         mClusterByteRanges[clusterNum].mEnd = (mCuesByteRange.mStart-1);
         done = true;
       } else {
         clusterNum++;
       }
     } while (!done);
   }
 #endif
 
   // We can't seek in buffered regions if we have no cues.
   bool haveCues;
   int64_t dummy = -1;
-  haveCues = nestegg_get_cue_point(mContext, 0, -1, &dummy, &dummy) == 0;
+  haveCues = nestegg_get_cue_point(mContext, 0, -1, &dummy, &dummy,
+                                   (uint64_t*)&dummy) == 0;
   mDecoder->SetMediaSeekable(haveCues);
 
   *aInfo = mInfo;
 
   *aTags = nullptr;
 
 #ifdef MOZ_DASH
   mDecoder->OnReadMetadataCompleted();
@@ -917,25 +932,47 @@ nsresult WebMReader::GetBuffered(nsTimeR
 }
 
 void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
 {
   mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
 }
 
 #ifdef MOZ_DASH
+int64_t
+WebMReader::GetSubsegmentForSeekTime(int64_t aSeekToTime)
+{
+  NS_ENSURE_TRUE(0 <= aSeekToTime, -1);
+  // Check the first n-1 subsegments. End time is the start time of the next
+  // subsegment.
+  for (uint32_t i = 1; i < (mClusterByteRanges.Length()); i++) {
+    if (aSeekToTime < mClusterByteRanges[i].mStartTime) {
+      return i-1;
+    }
+  }
+  // Check the last subsegment. End time is the end time of the file.
+  NS_ASSERTION(mDecoder, "Decoder should not be null!");
+  if (aSeekToTime <= mDecoder->GetMediaDuration()) {
+    return mClusterByteRanges.Length()-1;
+  }
+
+  return (-1);
+}
 nsresult
 WebMReader::GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges)
 {
   NS_ENSURE_TRUE(mContext, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aByteRanges.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
   NS_ENSURE_FALSE(mClusterByteRanges.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_FALSE(mCuesByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
 
-  aByteRanges = mClusterByteRanges;
+  for (uint32_t i = 0; i < mClusterByteRanges.Length(); i++) {
+    aByteRanges.AppendElement();
+    aByteRanges[i] = mClusterByteRanges[i];
+  }
 
   return NS_OK;
 }
 
 void
 WebMReader::RequestSwitchAtSubsegment(int32_t aSubsegmentIdx,
                                       MediaDecoderReader* aNextReader)
 {
@@ -944,16 +981,17 @@ WebMReader::RequestSwitchAtSubsegment(in
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   // Only allow one switch at a time; ignore if one is already requested.
   if (mSwitchingCluster != -1) {
     return;
   }
   NS_ENSURE_TRUE((uint32_t)aSubsegmentIdx < mClusterByteRanges.Length(), );
   mSwitchingCluster = aSubsegmentIdx;
+  NS_ENSURE_TRUE(aNextReader, );
   NS_ENSURE_TRUE(aNextReader != this, );
   mNextReader = static_cast<WebMReader*>(aNextReader);
 }
 
 void
 WebMReader::RequestSeekToSubsegment(uint32_t aIdx)
 {
   NS_ASSERTION(NS_IsMainThread() || mDecoder->OnDecodeThread(),
@@ -1022,10 +1060,8 @@ WebMReader::HasReachedSubsegment(uint32_
     mReachedSwitchAccessPoint = false;
     return true;
   }
   return false;
 }
 #endif /* MOZ_DASH */
 
 } // namespace mozilla
-
-
--- a/content/media/webm/WebMReader.h
+++ b/content/media/webm/WebMReader.h
@@ -176,16 +176,19 @@ public:
     mInitByteRange = aByteRange;
   }
 
   // Sets byte range for cue points, i.e. cluster offsets; used by DASH.
   void SetIndexByteRange(MediaByteRange &aByteRange) MOZ_OVERRIDE {
     mCuesByteRange = aByteRange;
   }
 
+  // Returns the index of the subsegment which contains the seek time.
+  int64_t GetSubsegmentForSeekTime(int64_t aSeekToTime) MOZ_OVERRIDE;
+
   // Returns list of ranges for cluster start and end offsets.
   nsresult GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges)
                                                                   MOZ_OVERRIDE;
 
   // Called by |DASHReader|::|PossiblySwitchVideoReaders| to check if this
   // reader has reached a switch access point and it's ok to switch readers.
   // Called on the decode thread.
   bool HasReachedSubsegment(uint32_t aSubsegmentIndex) MOZ_OVERRIDE;
@@ -299,17 +302,17 @@ private:
 #ifdef MOZ_DASH
   // Byte range for initialisation data; e.g. specified in DASH manifest.
   MediaByteRange mInitByteRange;
 
   // Byte range for cues; e.g. specified in DASH manifest.
   MediaByteRange mCuesByteRange;
 
   // Byte ranges for clusters; set internally, derived from cues.
-  nsTArray<MediaByteRange> mClusterByteRanges;
+  nsTArray<TimestampedMediaByteRange> mClusterByteRanges;
 
   // Pointer to the main |DASHReader|. Set in the constructor.
   DASHReader* mMainReader;
 
   // Index of the cluster to switch to. Monitor must be entered for write
   // access on all threads, read access off the decode thread.
   int32_t mSwitchingCluster;
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1956,16 +1956,24 @@ nsDocShell::GetChannelIsUnsafe(bool *aUn
     if (!jarChannel) {
         return NS_OK;
     }
 
     return jarChannel->GetIsUnsafe(aUnsafe);
 }
 
 NS_IMETHODIMP
+nsDocShell::GetHasMixedActiveContentLoaded(bool *aHasMixedActiveContentLoaded)
+{
+    nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
+    *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetAllowPlugins(bool * aAllowPlugins)
 {
     NS_ENSURE_ARG_POINTER(aAllowPlugins);
 
     *aAllowPlugins = mAllowPlugins;
     if (!mAllowPlugins) {
         return NS_OK;
     }
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -34,17 +34,17 @@ interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 
-[scriptable, builtinclass, uuid(a106db7f-6449-4a6b-914f-834ba308c3e2)]
+[scriptable, builtinclass, uuid(ed04b29f-ae9a-460a-858e-960b523bb3a5)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -460,16 +460,28 @@ interface nsIDocShell : nsISupports
   /**
    * Find out if the currently loaded document came from a suspicious channel
    * (such as a JAR channel where the server-returned content type isn't a
    * known JAR type).
    */
   readonly attribute boolean channelIsUnsafe;
 
   /**
+   * This attribute determines whether Mixed Active Content is loaded on the
+   * document. When it is true, mixed active content was not blocked and has
+   * loaded on the page. When it is false, mixed active content has not loaded on
+   * the page, either because there was no mixed active content requests on the page
+   * or such requests were blocked by nsMixedContentBlocker.
+   * This boolean is set to true in nsMixedContentBlocker if Mixed Active Content
+   * is allowed (either explicitly on the page by the user or when the about:config
+   * setting security.mixed_content.block_active_content is set to false).
+   */
+  readonly attribute boolean hasMixedActiveContentLoaded;
+
+  /**
    * Disconnects this docshell's editor from its window, and stores the
    * editor data in the open document's session history entry.  This
    * should be called only during page transitions.
    */
   [noscript, notxpcom] void DetachEditorFromWindow();
 
   /**
    * If true, this browser is not visible in the traditional sense, but
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -19,16 +19,19 @@ Cu.import("resource://gre/modules/AppsUt
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
 Cu.import("resource://gre/modules/OfflineCacheInstaller.jsm");
 Cu.import("resource://gre/modules/SystemMessagePermissionsChecker.jsm");
 
 function debug(aMsg) {
   //dump("-*-*- Webapps.jsm : " + aMsg + "\n");
 }
 
+// Minimum delay between two progress events while downloading, in ms.
+const MIN_PROGRESS_EVENT_DELAY = 1000;
+
 const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
@@ -1667,37 +1670,42 @@ this.DOMApplicationRegistry = {
 
       let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath())
                                   .QueryInterface(Ci.nsIHttpChannel);
       self.downloads[aApp.manifestURL] =
         { channel:requestChannel,
           appId: id,
           previousState: aIsUpdate ? "installed" : "pending"
         };
+
+      let lastProgressTime = 0;
       requestChannel.notificationCallbacks = {
         QueryInterface: function notifQI(aIID) {
           if (aIID.equals(Ci.nsISupports)          ||
               aIID.equals(Ci.nsIProgressEventSink) ||
               aIID.equals(Ci.nsILoadContext))
             return this;
 
           throw Cr.NS_ERROR_NO_INTERFACE;
         },
         getInterface: function notifGI(aIID) {
           return this.QueryInterface(aIID);
         },
         onProgress: function notifProgress(aRequest, aContext,
                                            aProgress, aProgressMax) {
           debug("onProgress: " + aProgress + "/" + aProgressMax);
           app.progress = aProgress;
-          self.broadcastMessage("Webapps:PackageEvent",
-                                { type: "progress",
-                                  manifestURL: aApp.manifestURL,
-                                  progress: aProgress,
-                                  app: app });
+          let now = Date.now();
+          if (now - lastProgressTime > MIN_PROGRESS_EVENT_DELAY) {
+            self.broadcastMessage("Webapps:PackageEvent",
+                                  { type: "progress",
+                                    manifestURL: aApp.manifestURL,
+                                    app: app });
+            lastProgressTime = now;
+          }
         },
         onStatus: function notifStatus(aRequest, aContext, aStatus, aStatusArg) { },
 
         // nsILoadContext
         appId: app.installerAppId,
         isInBrowserElement: app.installerIsBrowser,
         usePrivateBrowsing: false,
         isContent: false,
@@ -2260,16 +2268,17 @@ this.DOMApplicationRegistry = {
 /**
  * Appcache download observer
  */
 let AppcacheObserver = function(aApp) {
   debug("Creating AppcacheObserver for " + aApp.origin +
         " - " + aApp.installState);
   this.app = aApp;
   this.startStatus = aApp.installState;
+  this.lastProgressTime = 0;
 };
 
 AppcacheObserver.prototype = {
   // nsIOfflineCacheUpdateObserver implementation
   updateStateChanged: function appObs_Update(aUpdate, aState) {
     let mustSave = false;
     let app = this.app;
 
@@ -2305,18 +2314,24 @@ AppcacheObserver.prototype = {
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_NOUPDATE:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED:
         aUpdate.removeObserver(this);
         setStatus("installed");
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
+        setStatus(this.startStatus);
+        break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS:
-        setStatus(this.startStatus);
+        let now = Date.now();
+        if (now - this.lastProgressTime > MIN_PROGRESS_EVENT_DELAY) {
+          setStatus(this.startStatus);
+          this.lastProgressTime = now;
+        }
         break;
     }
 
     // Status changed, update the stored version.
     if (mustSave) {
       DOMApplicationRegistry._saveApps();
     }
   },
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -277,20 +277,18 @@
 #include "nsIDOMCSSImportRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsIDOMCSSMozDocumentRule.h"
 #include "nsIDOMCSSSupportsRule.h"
 #include "nsIDOMMozCSSKeyframeRule.h"
 #include "nsIDOMMozCSSKeyframesRule.h"
 #include "nsIDOMCSSPageRule.h"
-#include "nsIDOMCSSPrimitiveValue.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMCSSStyleSheet.h"
-#include "nsDOMCSSValueList.h"
 #define MOZ_GENERATED_EVENTS_INCLUDES
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENTS_INCLUDES
 #include "nsIDOMDeviceMotionEvent.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMNodeIterator.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMXULDocument.h"
@@ -1012,18 +1010,16 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(CSSGroupRuleRuleList, nsCSSRuleListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MediaList, nsMediaListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(StyleSheetList, nsStyleSheetListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSStyleSheet, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(ROCSSPrimitiveValue, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // Range classes
   NS_DEFINE_CLASSINFO_DATA(Range, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Selection, nsDOMGenericSH,
                            DEFAULT_SCRIPTABLE_FLAGS)
 
   // XUL classes
@@ -1065,19 +1061,16 @@ static nsDOMClassInfoData sClassInfoData
   // DOM Chrome Window class.
   NS_DEFINE_CLASSINFO_DATA(ChromeWindow, nsWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
                            WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSRGBColor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  NS_DEFINE_CLASSINFO_DATA(CSSValueList, nsCSSValueListSH,
-                           ARRAY_SCRIPTABLE_FLAGS)
-
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(ContentList, HTMLCollection,
                                      nsDOMGenericSH,
                                      DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLStylesheetProcessingInstruction, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ImageDocument, nsHTMLDocumentSH,
@@ -3001,24 +2994,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(StyleSheetList, nsIDOMStyleSheetList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStyleSheetList)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(CSSStyleSheet, nsIDOMCSSStyleSheet)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleSheet)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ROCSSPrimitiveValue,
-                                      nsIDOMCSSPrimitiveValue)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSPrimitiveValue)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSValueList, nsIDOMCSSValueList)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSValueList)
-  DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSRect, nsIDOMRect)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMRect)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSRGBColor, nsIDOMRGBColor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMRGBColor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSRGBAColor)
@@ -10069,27 +10054,16 @@ nsStyleSheetListSH::GetItemAt(nsISupport
                               nsWrapperCache **aCache, nsresult *rv)
 {
   nsDOMStyleSheetList* list = nsDOMStyleSheetList::FromSupports(aNative);
 
   return list->GetItemAt(aIndex);
 }
 
 
-// CSSValueList helper
-
-nsISupports*
-nsCSSValueListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
-                            nsWrapperCache **aCache, nsresult *aResult)
-{
-  nsDOMCSSValueList* list = nsDOMCSSValueList::FromSupports(aNative);
-
-  return list->GetItemAt(aIndex);
-}
-
 // CSSRuleList scriptable helper
 
 nsISupports*
 nsCSSRuleListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                            nsWrapperCache **aCache, nsresult *aResult)
 {
   nsICSSRuleList* list = static_cast<nsICSSRuleList*>(aNative);
 #ifdef DEBUG
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -1094,39 +1094,16 @@ protected:
 public:
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsStyleSheetListSH(aData);
   }
 };
 
 
-// CSSValueList helper
-
-class nsCSSValueListSH : public nsArraySH
-{
-protected:
-  nsCSSValueListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
-  {
-  }
-
-  virtual ~nsCSSValueListSH()
-  {
-  }
-
-  virtual nsISupports* GetItemAt(nsISupports *aNative, uint32_t aIndex,
-                                 nsWrapperCache **aCache, nsresult *aResult);
-
-public:
-  static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
-  {
-    return new nsCSSValueListSH(aData);
-  }
-};
-
 // CSSRuleList helper
 
 class nsCSSRuleListSH : public nsArraySH
 {
 protected:
   nsCSSRuleListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
   {
   }
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -123,17 +123,16 @@ DOMCI_CLASS(CSSCharsetRule)
 DOMCI_CLASS(CSSImportRule)
 DOMCI_CLASS(CSSMediaRule)
 DOMCI_CLASS(CSSNameSpaceRule)
 DOMCI_CLASS(CSSRuleList)
 DOMCI_CLASS(CSSGroupRuleRuleList)
 DOMCI_CLASS(MediaList)
 DOMCI_CLASS(StyleSheetList)
 DOMCI_CLASS(CSSStyleSheet)
-DOMCI_CLASS(ROCSSPrimitiveValue)
 
 // Range classes
 DOMCI_CLASS(Range)
 DOMCI_CLASS(Selection)
 
 // XUL classes
 #ifdef MOZ_XUL
 DOMCI_CLASS(XULDocument)
@@ -158,20 +157,16 @@ DOMCI_CLASS(TreeWalker)
 DOMCI_CLASS(CSSRect)
 
 // DOM Chrome Window class, almost identical to Window
 DOMCI_CLASS(ChromeWindow)
 
 // RGBColor object used by getComputedStyle
 DOMCI_CLASS(CSSRGBColor)
 
-// CSSValueList object that represents an nsIDOMCSSValueList, used
-// by DOM CSS
-DOMCI_CLASS(CSSValueList)
-
 // ContentList object used for various live NodeLists
 DOMCI_CLASS(ContentList)
   
 // Processing-instruction with target "xml-stylesheet"
 DOMCI_CLASS(XMLStylesheetProcessingInstruction)
   
 DOMCI_CLASS(ImageDocument)
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -143,20 +143,32 @@ DOMInterfaces = {
     'headerFile': 'nsClientRect.h',
     'resultNotAddRefed': [ 'item' ]
 },
 
 'CSS2Properties': {
   'nativeType': 'nsDOMCSSDeclaration'
 },
 
+"CSSPrimitiveValue": {
+  "nativeType": "nsROCSSPrimitiveValue"
+},
+
 'CSSStyleDeclaration': {
   'nativeType': 'nsICSSDeclaration'
 },
 
+"CSSValue": {
+  "concrete": False
+},
+
+"CSSValueList": {
+  "nativeType": "nsDOMCSSValueList",
+},
+
 'DelayNode': [
 {
     'resultNotAddRefed': [ 'delayTime' ],
 }],
 
 'Document': [
 {
     'nativeType': 'nsIDocument',
@@ -753,18 +765,18 @@ addExternalHTMLElement('HTMLImageElement
 addExternalHTMLElement('HTMLMenuElement')
 addExternalHTMLElement('HTMLOptionElement')
 addExternalHTMLElement('HTMLOptGroupElement')
 addExternalHTMLElement('HTMLVideoElement')
 addExternalIface('Attr')
 addExternalIface('CanvasGradient', headerFile='nsIDOMCanvasRenderingContext2D.h')
 addExternalIface('CanvasPattern', headerFile='nsIDOMCanvasRenderingContext2D.h')
 addExternalIface('ClientRect')
+addExternalIface("Counter")
 addExternalIface('CSSRule')
-addExternalIface('CSSValue')
 addExternalIface('DocumentType', nativeType='nsIDOMDocumentType')
 addExternalIface('DOMRequest')
 addExternalIface('DOMStringList', nativeType='nsDOMStringList',
                  headerFile='nsDOMLists.h')
 addExternalIface('File')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('LockedFile')
 addExternalIface('MediaStream')
@@ -775,15 +787,17 @@ addExternalIface('PaintRequest')
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('SVGLength')
 addExternalIface('SVGMatrix')
 addExternalIface('SVGNumber')
 addExternalIface('SVGPathSeg')
 addExternalIface('SVGPoint')
 addExternalIface('SVGTransform')
+addExternalIface("Rect")
+addExternalIface("RGBColor")
 addExternalIface('TextMetrics', headerFile='nsIDOMCanvasRenderingContext2D.h')
 addExternalIface('Touch', headerFile='nsIDOMTouchEvent.h')
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
 addExternalIface('UserDataHandler')
 addExternalIface('Window')
 addExternalIface('XULElement')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -34,16 +34,17 @@ def replaceFileIfChanged(filename, newCo
         pass
 
     if newContents == oldFileContents:
         return False
 
     f = open(filename, 'wb')
     f.write(newContents)
     f.close()
+    return True
 
 def toStringBool(arg):
     return str(not not arg).lower()
 
 def toBindingNamespace(arg):
     return re.sub("((_workers)?$)", "Binding\\1", arg);
 
 class CGThing():
@@ -4211,16 +4212,17 @@ class CGGenericMethod(CGAbstractBindingM
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
 
     def generate_code(self):
         return CGIndenter(CGGeneric(
             "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+            "MOZ_ASSERT(info->type == JSJitInfo::Method);\n"
             "JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
             "return method(cx, obj, self, argc, vp);"))
 
 class CGSpecializedMethod(CGAbstractStaticMethod):
     """
     A class for generating the C++ code for a specialized method that the JIT
     can call with lower overhead.
     """
@@ -4298,16 +4300,17 @@ class CGGenericGetter(CGAbstractBindingM
             name = "genericGetter"
             unwrapFailureCode = None
         CGAbstractBindingMethod.__init__(self, descriptor, name, args,
                                          unwrapFailureCode)
 
     def generate_code(self):
         return CGIndenter(CGGeneric(
             "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+            "MOZ_ASSERT(info->type == JSJitInfo::Getter);\n"
             "JSJitPropertyOp getter = info->op;\n"
             "return getter(cx, obj, self, vp);"))
 
 class CGSpecializedGetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute getter
     that the JIT can call with lower overhead.
     """
@@ -4377,16 +4380,17 @@ class CGGenericSetter(CGAbstractBindingM
     def generate_code(self):
         return CGIndenter(CGGeneric(
                 "JS::Value* argv = JS_ARGV(cx, vp);\n"
                 "JS::Value undef = JS::UndefinedValue();\n"
                 "if (argc == 0) {\n"
                 "  argv = &undef;\n"
                 "}\n"
                 "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+                "MOZ_ASSERT(info->type == JSJitInfo::Setter);\n"
                 "JSJitPropertyOp setter = info->op;\n"
                 "if (!setter(cx, obj, self, argv)) {\n"
                 "  return false;\n"
                 "}\n"
                 "*vp = JSVAL_VOID;\n"
                 "return true;"))
 
 class CGSpecializedSetter(CGAbstractStaticMethod):
@@ -4472,43 +4476,47 @@ class CGMemberJITInfo(CGThing):
     """
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
-    def defineJitInfo(self, infoName, opName, infallible, constant):
+    def defineJitInfo(self, infoName, opName, opType, infallible, constant):
         protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = toStringBool(infallible)
         conststr = toStringBool(constant)
         return ("\n"
                 "const JSJitInfo %s = {\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,\n"
+                "  JSJitInfo::%s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
                 "  %s  /* isConstant. Only relevant for getters. */\n"
-                "};\n" % (infoName, opName, protoID, depth, failstr, conststr))
+                "};\n" % (infoName, opName, protoID, depth, opType, failstr,
+                          conststr))
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             getterconst = self.member.getExtendedAttribute("Constant")
-            result = self.defineJitInfo(getterinfo, getter, getterinfal, getterconst)
+            result = self.defineJitInfo(getterinfo, getter, "Getter",
+                                        getterinfal, getterconst)
             if not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None:
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
-                result += self.defineJitInfo(setterinfo, setter, False, False)
+                result += self.defineJitInfo(setterinfo, setter, "Setter",
+                                             False, False)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             name = CppKeywords.checkMethodName(self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
             method = ("(JSJitPropertyOp)%s" % name)
 
             # Methods are infallible if they are infallible, have no arguments
@@ -4519,17 +4527,18 @@ class CGMemberJITInfo(CGThing):
             if len(sigs) == 1:
                 # Don't handle overloading. If there's more than one signature,
                 # one of them must take arguments.
                 sig = sigs[0]
                 if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
                     # No arguments and infallible return boxing
                     methodInfal = True
 
-            result = self.defineJitInfo(methodinfo, method, methodInfal, False)
+            result = self.defineJitInfo(methodinfo, method, "Method",
+                                        methodInfal, False)
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
 def getEnumValueName(value):
     # Some enum values can be empty strings.  Others might have weird
     # characters in them.  Deal with the former by returning "_empty",
     # deal with possible name collisions from that by throwing if the
     # enum value is actually "_empty", and throw on any value
@@ -7772,16 +7781,20 @@ class GlobalGenRoots():
 
         # Wrap all of that in our namespaces.
         idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
                                    CGWrapper(idEnum, pre='\n'))
         idEnum = CGWrapper(idEnum, post='\n')
 
         curr = CGList([idEnum])
 
+        # Let things know the maximum length of the prototype chain.
+        maxMacro = CGGeneric(declare="#define MAX_PROTOTYPE_CHAIN_LENGTH " + str(config.maxProtoChainLength))
+        curr.append(CGWrapper(maxMacro, post='\n\n'))
+
         # Constructor ID enum.
         constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
         idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
                                   ['prototypes::id::_ID_Count', '_ID_Start'])
 
         # Wrap all of that in our namespaces.
         idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
                                    CGWrapper(idEnum, pre='\n'))
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -128,17 +128,17 @@ enum DOMObjectType {
   eInterface,
   eInterfacePrototype
 };
 
 struct DOMClass
 {
   // A list of interfaces that this object implements, in order of decreasing
   // derivedness.
-  const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count];
+  const prototypes::ID mInterfaceChain[MAX_PROTOTYPE_CHAIN_LENGTH];
 
   // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in
   // the proxy private if we use a proxy object.
   // Sometimes it's an nsISupports and sometimes it's not; this class tells
   // us which it is.
   const bool mDOMObjectIsISupports;
 
   const NativePropertyHooks* mNativeHooks;
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -59,16 +59,17 @@ MOCHITEST_FILES := \
   test_traceProtos.html \
   test_forOf.html \
   forOf_iframe.html \
   test_sequence_wrapping.html \
   file_bug775543.html \
   test_bug788369.html \
   test_bug742191.html \
   test_namedNoIndexed.html \
+  test_bug759621.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_bug775543.html \
   $(NULL)
 
 # Include rules.mk before any of our targets so our first target is coming from
 # rules.mk and running make with no target in this dir does the right thing.
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug759621.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759621
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 759621</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=759621">Mozilla Bug 759621</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 759621 **/
+var l = document.getElementsByTagName("*");
+l.namedItem = "pass";
+is(l.namedItem, "pass", "Should be able to set expando shadowing a proto prop");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -183,20 +183,17 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
 
   nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
   NS_ASSERTION(databaseInfo, "Null pointer!");
 
   nsRefPtr<IDBDatabase> db(new IDBDatabase());
 
   db->BindToOwner(aOwnerCache);
-  if (!db->SetScriptOwner(aOwnerCache->GetScriptOwner())) {
-    return nullptr;
-  }
-
+  db->SetScriptOwner(aOwnerCache->GetScriptOwner());
   db->mFactory = aFactory;
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
   db->mContentParent = aContentParent;
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -52,20 +52,17 @@ IDBRequest::Create(nsISupports* aSource,
                    JSContext* aCallingCx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   nsRefPtr<IDBRequest> request(new IDBRequest());
 
   request->mSource = aSource;
   request->mTransaction = aTransaction;
   request->BindToOwner(aOwnerCache);
-  if (!request->SetScriptOwner(aOwnerCache->GetScriptOwner())) {
-    return nullptr;
-  }
-
+  request->SetScriptOwner(aOwnerCache->GetScriptOwner());
   request->CaptureCaller(aCallingCx);
 
   return request.forget();
 }
 
 void
 IDBRequest::Reset()
 {
@@ -347,20 +344,17 @@ IDBOpenDBRequest::Create(IDBFactory* aFa
                          JSContext* aCallingCx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aFactory, "Null pointer!");
 
   nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest();
 
   request->BindToOwner(aOwner);
-  if (!request->SetScriptOwner(aScriptOwner)) {
-    return nullptr;
-  }
-
+  request->SetScriptOwner(aScriptOwner);
   request->CaptureCaller(aCallingCx);
   request->mFactory = aFactory;
 
   return request.forget();
 }
 
 void
 IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction)
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -98,20 +98,17 @@ IDBTransaction::CreateInternal(IDBDataba
   NS_ASSERTION(!aIsVersionChangeTransactionChild ||
                (!IndexedDatabaseManager::IsMainProcess() &&
                 aMode == IDBTransaction::VERSION_CHANGE),
                "Busted logic!");
 
   nsRefPtr<IDBTransaction> transaction = new IDBTransaction();
 
   transaction->BindToOwner(aDatabase);
-  if (!transaction->SetScriptOwner(aDatabase->GetScriptOwner())) {
-    return nullptr;
-  }
-
+  transaction->SetScriptOwner(aDatabase->GetScriptOwner());
   transaction->mDatabase = aDatabase;
   transaction->mMode = aMode;
   transaction->mDatabaseInfo = aDatabase->Info();
   transaction->mObjectStoreNames.AppendElements(aObjectStoreNames);
   transaction->mObjectStoreNames.Sort();
 
   IndexedDBTransactionChild* actor = nullptr;
 
--- a/dom/indexedDB/IDBWrapperCache.cpp
+++ b/dom/indexedDB/IDBWrapperCache.cpp
@@ -40,29 +40,27 @@ NS_IMPL_RELEASE_INHERITED(IDBWrapperCach
 
 IDBWrapperCache::~IDBWrapperCache()
 {
   mScriptOwner = nullptr;
   nsContentUtils::ReleaseWrapper(this, this);
   NS_DROP_JS_OBJECTS(this, IDBWrapperCache);
 }
 
-bool
+void
 IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner)
 {
   NS_ASSERTION(aScriptOwner, "This should never be null!");
 
   mScriptOwner = aScriptOwner;
 
   nsISupports* thisSupports = NS_CYCLE_COLLECTION_UPCAST(this, IDBWrapperCache);
   nsXPCOMCycleCollectionParticipant* participant;
   CallQueryInterface(this, &participant);
   nsContentUtils::HoldJSObjects(thisSupports, participant);
-
-  return true;
 }
 
 #ifdef DEBUG
 void
 IDBWrapperCache::AssertIsRooted() const
 {
   NS_ASSERTION(nsContentUtils::AreJSObjectsHeld(const_cast<IDBWrapperCache*>(this)),
                "Why aren't we rooted?!");
--- a/dom/indexedDB/IDBWrapperCache.h
+++ b/dom/indexedDB/IDBWrapperCache.h
@@ -20,17 +20,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
                                                    IDBWrapperCache,
                                                    nsDOMEventTargetHelper)
 
   JSObject* GetScriptOwner() const
   {
     return mScriptOwner;
   }
-  bool SetScriptOwner(JSObject* aScriptOwner);
+  void SetScriptOwner(JSObject* aScriptOwner);
 
   JSObject* GetParentObject()
   {
     if (mScriptOwner) {
       return mScriptOwner;
     }
 
     // Do what nsEventTargetSH::PreCreate does.
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -62,17 +62,16 @@ interface nsIDOMEventListener;
 // HTML
 interface nsIDOMHTMLElement;
 interface nsIDOMHTMLFormElement;
 interface nsIDOMHTMLCollection;
 interface nsIDOMHTMLHeadElement;
 
 // CSS
 interface nsIDOMCSSValue;
-interface nsIDOMCSSValueList;
 interface nsIDOMCSSPrimitiveValue;
 interface nsIDOMCSSRule;
 interface nsIDOMCSSRuleList;
 interface nsIDOMMozCSSKeyframeRule;
 interface nsIDOMCSSStyleSheet;
 interface nsIDOMCSSStyleDeclaration;
 interface nsIDOMCounter;
 interface nsIDOMRect;
--- a/dom/interfaces/css/Makefile.in
+++ b/dom/interfaces/css/Makefile.in
@@ -16,17 +16,16 @@ GRE_MODULE	= 1
 
 SDK_XPIDLSRCS = 				\
 	nsIDOMCSSPrimitiveValue.idl		\
 	nsIDOMCSSRule.idl			\
 	nsIDOMCSSRuleList.idl			\
 	nsIDOMCSSStyleDeclaration.idl		\
 	nsIDOMCSSStyleSheet.idl			\
 	nsIDOMCSSValue.idl			\
-	nsIDOMCSSValueList.idl			\
 	nsIDOMElementCSSInlineStyle.idl		\
 	$(NULL)
 
 XPIDLSRCS =					\
 	nsIDOMCSSCharsetRule.idl		\
 	nsIDOMCSSConditionRule.idl		\
 	nsIDOMCSSFontFaceRule.idl		\
 	nsIDOMCSSGroupingRule.idl		\
deleted file mode 100644
--- a/dom/interfaces/css/nsIDOMCSSValueList.idl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMCSSValue.idl"
-
-/**
- * The nsIDOMCSSValueList interface is a datatype for a list of CSS
- * values in the Document Object Model.
- *
- * For more information on this interface please see
- * http://www.w3.org/TR/DOM-Level-2-Style
- */
-
-[scriptable, uuid(8f09fa84-39b9-4dca-9b2f-db0eeb186286)]
-interface nsIDOMCSSValueList : nsIDOMCSSValue
-{
-  readonly attribute unsigned long    length;
-  nsIDOMCSSValue     item(in unsigned long index);
-};
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -108,16 +108,17 @@ private:
   // mProcessPriority tracks the priority we've given this process in hal,
   // except that, when the grace period timer is active,
   // mProcessPriority == BACKGROUND even though hal still thinks we're a
   // foreground process.
   ProcessPriority mProcessPriority;
 
   nsTArray<nsWeakPtr> mWindows;
   nsCOMPtr<nsITimer> mGracePeriodTimer;
+  nsWeakPtr mMemoryMinimizerRunnable;
   TimeStamp mStartupTime;
 };
 
 NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener);
 
 ProcessPriorityManager::ProcessPriorityManager()
   : mProcessPriority(PROCESS_PRIORITY_FOREGROUND)
   , mStartupTime(TimeStamp::Now())
@@ -278,16 +279,23 @@ ProcessPriorityManager::SetPriority(Proc
   } else if (aPriority == PROCESS_PRIORITY_FOREGROUND) {
     // If this is a background --> foreground transition, do it immediately, and
     // cancel the outstanding grace period timer, if there is one.
     if (mGracePeriodTimer) {
       mGracePeriodTimer->Cancel();
       mGracePeriodTimer = nullptr;
     }
 
+    // Cancel the memory minimization procedure we might have started.
+    nsCOMPtr<nsICancelableRunnable> runnable =
+      do_QueryReferent(mMemoryMinimizerRunnable);
+    if (runnable) {
+      runnable->Cancel();
+    }
+
     LOG("Setting priority to %d.", aPriority);
     mProcessPriority = aPriority;
     hal::SetProcessPriority(getpid(), aPriority);
 
   } else {
     MOZ_ASSERT(false);
   }
 }
@@ -305,17 +313,27 @@ ProcessPriorityManager::OnGracePeriodTim
 
   mGracePeriodTimer = nullptr;
   hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_BACKGROUND);
 
   // We're in the background; dump as much memory as we can.
   nsCOMPtr<nsIMemoryReporterManager> mgr =
     do_GetService("@mozilla.org/memory-reporter-manager;1");
   if (mgr) {
-    mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
+    nsCOMPtr<nsICancelableRunnable> runnable =
+      do_QueryReferent(mMemoryMinimizerRunnable);
+
+    // Cancel the previous task if it's still pending
+    if (runnable) {
+      runnable->Cancel();
+    }
+
+    mgr->MinimizeMemoryUsage(/* callback = */ nullptr,
+                             getter_AddRefs(runnable));
+    mMemoryMinimizerRunnable = do_GetWeakReference(runnable);
   }
 }
 
 } // anonymous namespace
 
 void
 InitProcessPriorityManager()
 {
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/crashtests/799419.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=799419
+-->
+<head>
+  <meta charset="utf-8">
+  <title>2 Peer Connections Create and Close + Fake Video</title>
+  <script type="application/javascript">
+    function boom() {
+      var v0 = mozRTCPeerConnection();
+      var v1 = mozRTCPeerConnection();
+      var v2 = document.getElementById("pc1video");
+      var v3 = document.getElementById("pc2video");
+      navigator.mozGetUserMedia({video:true, fake: true},
+        function(stream) {}, function() {});
+      v0.close();
+      v1.close();
+    }
+  </script>
+<body onload="boom()">
+<video id="pc1video" width="100" height="100" controls></video>
+<video id="pc2video" width="100" height="100" controls></video>
+</body>
+</html>
--- a/dom/media/tests/crashtests/crashtests.list
+++ b/dom/media/tests/crashtests/crashtests.list
@@ -1,9 +1,10 @@
 default-preferences pref(media.peerconnection.enabled,true)
 
 load 780790.html
 load 791270.html
 load 791278.html
 load 791330.html
+load 799419.html
 load 801227.html
 load 802982.html
 load 812785.html
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -12,19 +12,22 @@ include $(DEPTH)/config/autoconf.mk
 
 # Bug 814718 prevents us from running the following test:
 #  test_getUserMedia_basicVideo.html
 
 MOCHITEST_FILES = \
   test_getUserMedia_exceptions.html \
   head.js \
   mediaStreamPlayback.js \
+  pc.js \
   $(NULL)
 
 # The following tests are leaking and cannot be run by default yet
 ifdef MOZ_WEBRTC_LEAKING_TESTS
 MOCHITEST_FILES += \
   test_getUserMedia_basicAudio.html \
   test_getUserMedia_basicVideoAudio.html \
+  test_peerConnection_basicAudio.html \
+  test_peerConnection_basicVideo.html \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/pc.js
@@ -0,0 +1,44 @@
+/* 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/. */
+
+
+var PeerConnection = {
+  pc1_offer : null,
+  pc2_answer : null,
+
+  handShake: function PC_handShake(aPCLocal, aPCRemote, aSuccessCallback) {
+
+    function onCreateOfferSuccess(aOffer) {
+      pc1_offer = aOffer;
+      aPCLocal.setLocalDescription(aOffer, onSetLocalDescriptionSuccess1,
+                                   unexpectedCallbackAndFinish);
+    }
+
+    function onSetLocalDescriptionSuccess1() {
+      aPCRemote.setRemoteDescription(pc1_offer, onSetRemoteDescriptionSuccess1,
+                                     unexpectedCallbackAndFinish);
+    }
+
+    function onSetRemoteDescriptionSuccess1() {
+      aPCRemote.createAnswer(onCreateAnswerSuccess, unexpectedCallbackAndFinish);
+    }
+
+    function onCreateAnswerSuccess(aAnswer) {
+      pc2_answer = aAnswer;
+      aPCRemote.setLocalDescription(aAnswer, onSetLocalDescriptionSuccess2,
+                                    unexpectedCallbackAndFinish);
+    }
+
+    function onSetLocalDescriptionSuccess2() {
+      aPCLocal.setRemoteDescription(pc2_answer, onSetRemoteDescriptionSuccess2,
+                                    unexpectedCallbackAndFinish);
+    }
+
+    function onSetRemoteDescriptionSuccess2() {
+      aSuccessCallback();
+    }
+
+    aPCLocal.createOffer(onCreateOfferSuccess, unexpectedCallbackAndFinish);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=796892
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Basic audio-only peer connection</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=796888">Basic audio-only peer connection</a>
+<p id="display"></p>
+<div id="content" style="">
+  <audio id="audioPCLocal" controls></audio>
+  <audio id="audioPCRemote" controls></audio>
+  <audio id="audioLocal" controls></audio>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  var audioLocal;
+  var audioPCLocal;
+  var audioPCRemote;
+
+  var pcLocal;
+  var pcRemote;
+
+  var test_data = {
+    pcLocal: { audio: [], video: []},
+    pcRemote: { audio: [], video: []}
+  };
+
+  runTest(function () {
+    audioLocal = document.getElementById("audioLocal");
+    audioPCLocal = document.getElementById("audioPCLocal");
+    audioPCRemote = document.getElementById("audioPCRemote");
+
+    pcLocal = new mozRTCPeerConnection();
+    pcRemote = new mozRTCPeerConnection();
+
+    pcLocal.onaddstream = function (aObj) {
+      test_data.pcLocal[aObj.type].push(aObj.stream);
+
+      if (aObj.type === "audio") {
+        audioPCRemote.mozSrcObject = aObj.stream;
+        audioPCRemote.play();
+      }
+    };
+
+    pcRemote.onaddstream = function (aObj) {
+      test_data.pcRemote[aObj.type].push(aObj.stream);
+
+      if (aObj.type === "audio") {
+        audioPCLocal.mozSrcObject = aObj.stream;
+        audioPCLocal.play();
+      }
+    };
+
+    navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aLocalInputStream) {
+      pcLocal.addStream(aLocalInputStream);
+
+      navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aRemoteInputStream) {
+        pcRemote.addStream(aRemoteInputStream);
+
+        audioLocal.mozSrcObject = aLocalInputStream;
+        audioLocal.play();
+
+        PeerConnection.handShake(pcLocal, pcRemote, function () {
+          is(pcLocal.localStreams.length, 1,
+             "A single local stream has been attached to the local peer");
+          is(pcRemote.localStreams.length, 1,
+             "A single local stream has been attached to the remote peer");
+
+          // Bug 816780 - onaddstream() gets called twice even if only audio or audio is requested
+          is(test_data.pcLocal.audio.length, 1,
+             "A remote audio stream has been attached to the local peer");
+          todo_is(test_data.pcLocal.video.length, 0,
+                  "No remote video stream has been attached to the local peer");
+          is(test_data.pcRemote.audio.length, 1,
+             "A remote audio stream has been attached to the remote peer");
+          todo_is(test_data.pcRemote.video.length, 0,
+                  "No remote video stream has been attached to the remote peer");
+
+          is(test_data.pcLocal.audio[0], pcLocal.remoteStreams[0],
+             "Remote stream for local peer is accessible");
+          is(test_data.pcRemote.audio[0], pcRemote.remoteStreams[0],
+             "Remote stream for remote peer is accessible");
+
+          info("For now simply disconnect. We will add checks for media in a follow-up bug");
+          disconnect();
+        });
+      }, unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  });
+
+  function disconnect() {
+    pcLocal.close();
+    pcRemote.close();
+
+    info("We can't run any checks and clean-up code due to a crash (see bug 820072)");
+
+    SimpleTest.finish();
+  }
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=796888
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Basic video-only peer connection</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=796888">Basic video-only peer connection</a>
+<p id="display"></p>
+<div id="content" style="">
+  <video id="videoPCLocal" width="160" height="120" controls></video>
+  <video id="videoPCRemote" width="160" height="120" controls></video>
+  <video id="videoLocal" width="160" height="120" controls></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  var videoLocal;
+  var videoPCLocal;
+  var videoPCRemote;
+
+  var pcLocal;
+  var pcRemote;
+
+  var test_data = {
+    pcLocal: { audio: [], video: []},
+    pcRemote: { audio: [], video: []}
+  };
+
+  runTest(function () {
+    videoLocal = document.getElementById("videoLocal");
+    videoPCLocal = document.getElementById("videoPCLocal");
+    videoPCRemote = document.getElementById("videoPCRemote");
+
+    pcLocal = new mozRTCPeerConnection();
+    pcRemote = new mozRTCPeerConnection();
+
+    pcLocal.onaddstream = function (aObj) {
+      test_data.pcLocal[aObj.type].push(aObj.stream);
+
+      if (aObj.type === "video") {
+        videoPCRemote.mozSrcObject = aObj.stream;
+        videoPCRemote.play();
+      }
+    };
+
+    pcRemote.onaddstream = function (aObj) {
+      test_data.pcRemote[aObj.type].push(aObj.stream);
+
+      if (aObj.type === "video") {
+        videoPCLocal.mozSrcObject = aObj.stream;
+        videoPCLocal.play();
+      }
+    };
+
+    navigator.mozGetUserMedia({video: true, fake: true}, function onSuccess(aLocalInputStream) {
+      pcLocal.addStream(aLocalInputStream);
+
+      navigator.mozGetUserMedia({video: true, fake: true}, function onSuccess(aRemoteInputStream) {
+        pcRemote.addStream(aRemoteInputStream);
+
+        videoLocal.mozSrcObject = aLocalInputStream;
+        videoLocal.play();
+
+        PeerConnection.handShake(pcLocal, pcRemote, function () {
+          is(pcLocal.localStreams.length, 1,
+             "A single local stream has been attached to the local peer");
+          is(pcRemote.localStreams.length, 1,
+             "A single local stream has been attached to the remote peer");
+
+          // Bug 816780 - onaddstream() gets called twice even if only video or audio is requested
+          is(test_data.pcLocal.video.length, 1,
+             "A remote audio stream has been attached to the local peer");
+          todo_is(test_data.pcLocal.audio.length, 0,
+                  "No remote video stream has been attached to the local peer");
+          is(test_data.pcRemote.video.length, 1,
+             "A remote audio stream has been attached to the remote peer");
+          todo_is(test_data.pcRemote.audio.length, 0,
+                  "No remote video stream has been attached to the remote peer");
+
+          todo_is(test_data.pcLocal.video[0], pcLocal.remoteStreams[0],
+                  "Remote stream for local peer is accessible");
+          todo_is(test_data.pcRemote.video[0], pcRemote.remoteStreams[0],
+                  "Remote stream for remote peer is accessible");
+
+          info("For now simply disconnect. We will add checks for media in a follow-up bug");
+          disconnect();
+        });
+      }, unexpectedCallbackAndFinish);
+    }, unexpectedCallbackAndFinish);
+  });
+
+  function disconnect() {
+    pcLocal.close();
+    pcRemote.close();
+
+    info("We can't run any checks and clean-up code due to a crash (see bug 820072)");
+
+    SimpleTest.finish();
+  }
+</script>
+</pre>
+</body>
+</html>
--- a/dom/phonenumberutils/PhoneNumber.jsm
+++ b/dom/phonenumberutils/PhoneNumber.jsm
@@ -265,17 +265,18 @@ this.PhoneNumber = (function (dataBase) 
 
     // Lookup the meta data for the region (or regions) and if the rest of
     // the number parses for that region, return the parsed number.
     var entry = dataBase[countryCode];
     if (Array.isArray(entry)) {
       for (var n = 0; n < entry.length; ++n) {
         if (typeof entry[n] == "string")
           entry[n] = ParseMetaData(countryCode, entry[n]);
-        if (ret = ParseNationalNumber(number, entry[n]))
+        ret = ParseNationalNumber(number, entry[n])
+        if (ret)
           return ret;
       }
       return null;
     }
     if (typeof entry == "string")
       entry = dataBase[countryCode] = ParseMetaData(countryCode, entry);
     return ParseNationalNumber(number, entry);
   }
@@ -307,49 +308,53 @@ this.PhoneNumber = (function (dataBase) 
     // Lookup the meta data for the given region.
     var md = FindMetaDataForRegion(defaultRegion.toUpperCase());
 
     // See if the number starts with an international prefix, and if the
     // number resulting from stripping the code is valid, then remove the
     // prefix and flag the number as international.
     if (md.internationalPrefix.test(number)) {
       var possibleNumber = number.replace(md.internationalPrefix, "");
-      if (ret = ParseInternationalNumber(possibleNumber))
+      ret = ParseInternationalNumber(possibleNumber)
+      if (ret)
         return ret;
     }
 
     // This is not an international number. See if its a national one for
     // the current region. National numbers can start with the national
     // prefix, or without.
     if (md.nationalPrefixForParsing) {
       // Some regions have specific national prefix parse rules. Apply those.
       var withoutPrefix = number.replace(md.nationalPrefixForParsing,
                                          md.nationalPrefixTransformRule);
-      if (ret = ParseNationalNumber(withoutPrefix, md))
+      ret = ParseNationalNumber(withoutPrefix, md)
+      if (ret)
         return ret;
     } else {
       // If there is no specific national prefix rule, just strip off the
       // national prefix from the beginning of the number (if there is one).
       var nationalPrefix = md.nationalPrefix;
       if (nationalPrefix && number.indexOf(nationalPrefix) == 0 &&
           (ret = ParseNationalNumber(number.substr(nationalPrefix.length), md))) {
         return ret;
       }
     }
-    if (ret = ParseNationalNumber(number, md))
+    ret = ParseNationalNumber(number, md)
+    if (ret)
       return ret;
 
     // If the number matches the possible numbers of the current region,
     // return it as a possible number.
     if (md.possiblePattern.test(number))
       return new NationalNumber(md, number);
 
     // Now lets see if maybe its an international number after all, but
     // without '+' or the international prefix.
-    if (ret = ParseInternationalNumber(number))
+    ret = ParseInternationalNumber(number)
+    if (ret)
       return ret;
 
     // We couldn't parse the number at all.
     return null;
   }
 
   return {
     Parse: ParseNumber,
--- a/dom/plugins/base/nsIPluginDocument.idl
+++ b/dom/plugins/base/nsIPluginDocument.idl
@@ -1,29 +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"
 #include "nsIStreamListener.idl"
 
-[uuid(e4be1d0a-9f24-4d69-bec5-245726ab85fb)]
+[uuid(a93a0f0f-24f0-4206-a21b-56a43dcbdd88)]
 interface nsIPluginDocument : nsISupports
 {
  /**
-  * Sets the stream listener for this plugin document 
-  */
-  void setStreamListener(in nsIStreamListener aStreamListener);
-
-  
- /**
   * Causes the plugin to print in full-page mode
   */
   void print();
-
-  /**
-   * Check whether the document is planning to handle plug-in instantiation
-   * itself.  If not, then the plugin content node should do it.
-   */
-  // XXXbz once we move plug-in loading to content, this can go away.
-  readonly attribute boolean willHandleInstantiation;
 };
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1379,27 +1379,53 @@ nsNPAPIPluginInstance::PrivateModeStateC
   PluginDestructionGuard guard(this);
     
   NPError error;
   NPBool value = static_cast<NPBool>(enabled);
   NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this);
   return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
 }
 
+class DelayUnscheduleEvent : public nsRunnable {
+public:
+  nsRefPtr<nsNPAPIPluginInstance> mInstance;
+  uint32_t mTimerID;
+  DelayUnscheduleEvent(nsNPAPIPluginInstance* aInstance, uint32_t aTimerId)
+    : mInstance(aInstance)
+    , mTimerID(aTimerId)
+  {}
+
+  ~DelayUnscheduleEvent() {}
+
+  NS_IMETHOD Run();
+};
+
+NS_IMETHODIMP
+DelayUnscheduleEvent::Run()
+{
+  mInstance->UnscheduleTimer(mTimerID);
+  return NS_OK;
+}
+
+
 static void
 PluginTimerCallback(nsITimer *aTimer, void *aClosure)
 {
   nsNPAPITimer* t = (nsNPAPITimer*)aClosure;
   NPP npp = t->npp;
   uint32_t id = t->id;
 
   PLUGIN_LOG(PLUGIN_LOG_NOISY, ("nsNPAPIPluginInstance running plugin timer callback this=%p\n", npp->ndata));
 
   MAIN_THREAD_JNI_REF_GUARD;
+  // Some plugins (Flash on Android) calls unscheduletimer
+  // from this callback.
+  t->inCallback = true;
   (*(t->callback))(npp, id);
+  t->inCallback = false;
 
   // Make sure we still have an instance and the timer is still alive
   // after the callback.
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
   if (!inst || !inst->TimerWithID(id, NULL))
     return;
 
   // use UnscheduleTimer to clean up if this is a one-shot timer
@@ -1426,16 +1452,17 @@ nsNPAPIPluginInstance::TimerWithID(uint3
 uint32_t
 nsNPAPIPluginInstance::ScheduleTimer(uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID))
 {
   if (RUNNING != mRunning)
     return 0;
 
   nsNPAPITimer *newTimer = new nsNPAPITimer();
 
+  newTimer->inCallback = false;
   newTimer->npp = &mNPP;
 
   // generate ID that is unique to this instance
   uint32_t uniqueID = mTimers.Length();
   while ((uniqueID == 0) || TimerWithID(uniqueID, NULL))
     uniqueID++;
   newTimer->id = uniqueID;
 
@@ -1463,16 +1490,22 @@ void
 nsNPAPIPluginInstance::UnscheduleTimer(uint32_t timerID)
 {
   // find the timer struct by ID
   uint32_t index;
   nsNPAPITimer* t = TimerWithID(timerID, &index);
   if (!t)
     return;
 
+  if (t->inCallback) {
+    nsCOMPtr<nsIRunnable> e = new DelayUnscheduleEvent(this, timerID);
+    NS_DispatchToCurrentThread(e);
+    return;
+  }
+
   // cancel the timer
   t->timer->Cancel();
 
   // remove timer struct from array
   mTimers.RemoveElementAt(index);
 
   // delete timer
   delete t;
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -52,16 +52,17 @@ const NPDrawingModel kDefaultDrawingMode
 
 class nsNPAPITimer
 {
 public:
   NPP npp;
   uint32_t id;
   nsCOMPtr<nsITimer> timer;
   void (*callback)(NPP npp, uint32_t timerID);
+  bool inCallback;
 };
 
 class nsNPAPIPluginInstance : public nsISupports
 {
 private:
   typedef mozilla::PluginLibrary PluginLibrary;
 
 public:
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -218,24 +218,20 @@ nsIFile *nsPluginHost::sPluginTempDir;
 nsPluginHost *nsPluginHost::sInst;
 
 NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
 
 nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
 : mFullPath(aFullPath),
   mLastModifiedTime(aLastModifiedTime),
   mSeen(false)
-{
-  
-}
+{}
 
 nsInvalidPluginTag::~nsInvalidPluginTag()
-{
-  
-}
+{}
 
 // Helper to check for a MIME in a comma-delimited preference
 static bool
 IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
 {
   nsAutoCString searchStr;
   searchStr.Assign(',');
   searchStr.Append(aTypeList);
@@ -900,34 +896,39 @@ nsPluginHost::GetPluginTempDir(nsIFile *
 
     tmpDir.swap(sPluginTempDir);
   }
 
   return sPluginTempDir->Clone(aDir);
 }
 
 nsresult
-nsPluginHost::InstantiateEmbeddedPluginInstance(const char *aMimeType, nsIURI* aURL,
-                                                nsObjectLoadingContent *aContent,
-                                                nsPluginInstanceOwner** aOwner)
+nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
+                                        nsObjectLoadingContent *aContent,
+                                        nsPluginInstanceOwner** aOwner)
 {
   NS_ENSURE_ARG_POINTER(aOwner);
 
 #ifdef PLUGIN_LOGGING
   nsAutoCString urlSpec;
   if (aURL)
     aURL->GetAsciiSpec(urlSpec);
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
-        ("nsPluginHost::InstantiateEmbeddedPlugin Begin mime=%s, url=%s\n",
+        ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
         aMimeType, urlSpec.get()));
 
   PR_LogFlush();
 #endif
 
+  if (!aMimeType) {
+    NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
+    return NS_ERROR_FAILURE;
+  }
+
   nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
   if (!instanceOwner) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
   nsresult rv = instanceOwner->Init(ourContent);
   if (NS_FAILED(rv)) {
@@ -947,187 +948,57 @@ nsPluginHost::InstantiateEmbeddedPluginI
   }
 
   if (tagType != nsPluginTagType_Embed &&
       tagType != nsPluginTagType_Applet &&
       tagType != nsPluginTagType_Object) {
     return NS_ERROR_FAILURE;
   }
 
-  // Security checks. Can't do security checks without a URI - hopefully the plugin
-  // will take care of that.
-  if (aURL) {
-    nsCOMPtr<nsIScriptSecurityManager> secMan =
-                    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-    if (NS_FAILED(rv))
-      return rv; // Better fail if we can't do security checks
-
-    nsCOMPtr<nsIDocument> doc;
-    instanceOwner->GetDocument(getter_AddRefs(doc));
-    if (!doc)
-      return NS_ERROR_NULL_POINTER;
-
-    rv = secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), aURL, 0);
-    if (NS_FAILED(rv))
-      return rv;
-
-    nsCOMPtr<nsIDOMElement> elem;
-    pti->GetDOMElement(getter_AddRefs(elem));
-
-    int16_t shouldLoad = nsIContentPolicy::ACCEPT; // default permit
-    nsresult rv =
-      NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
-                                aURL,
-                                doc->NodePrincipal(),
-                                elem,
-                                nsDependentCString(aMimeType ? aMimeType : ""),
-                                nullptr, //extra
-                                &shouldLoad);
-    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))
-      return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
-  }
-
   bool isJava = false;
   nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
   if (pluginTag) {
     isJava = pluginTag->mIsJavaPlugin;
   }
 
-  // Determine if the scheme of this URL is one we can handle internally because we should
-  // only open the initial stream if it's one that we can handle internally. Otherwise
-  // |NS_OpenURI| in |InstantiateEmbeddedPlugin| may open up a OS protocal registered helper app
-  // Also set bCanHandleInternally to true if aAllowOpeningStreams is
-  // false; we don't want to do any network traffic in that case.
-  bool bCanHandleInternally = false;
-  nsAutoCString scheme;
-  if (aURL && NS_SUCCEEDED(aURL->GetScheme(scheme))) {
-      nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
-      contractID += scheme;
-      ToLowerCase(contractID);
-      nsCOMPtr<nsIProtocolHandler> handler = do_GetService(contractID.get());
-      if (handler)
-        bCanHandleInternally = true;
-  }
-
-  // if we don't have a MIME type at this point, we still have one more chance by
-  // opening the stream and seeing if the server hands one back
-  if (!aMimeType) {
-    if (bCanHandleInternally && !aContent->SrcStreamLoading()) {
-      NewEmbeddedPluginStream(aURL, aContent, nullptr);
-    }
-    return NS_ERROR_FAILURE;
-  }
-
   rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<nsNPAPIPluginInstance> instance;
   rv = instanceOwner->GetInstance(getter_AddRefs(instance));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (instance) {
     instanceOwner->CreateWidget();
 
     // If we've got a native window, the let the plugin know about it.
     instanceOwner->CallSetWindow();
-
-    // create an initial stream with data
-    // don't make the stream if it's a java applet or we don't have SRC or DATA attribute
-    // no need to check for "data" as it would have been converted to "src"
-    const char *value;
-    bool havedata = NS_SUCCEEDED(pti->GetAttribute("SRC", &value));
-    if (havedata && !isJava && bCanHandleInternally && !aContent->SrcStreamLoading()) {
-      NewEmbeddedPluginStream(aURL, aContent, instance.get());
-    }
   }
 
   // At this point we consider instantiation to be successful. Do not return an error.
   instanceOwner.forget(aOwner);
 
 #ifdef PLUGIN_LOGGING
   nsAutoCString urlSpec2;
   if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
-        ("nsPluginHost::InstantiateEmbeddedPlugin Finished mime=%s, rv=%d, url=%s\n",
+        ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
         aMimeType, rv, urlSpec2.get()));
 
   PR_LogFlush();
 #endif
 
   return NS_OK;
 }
 
-nsresult nsPluginHost::InstantiateFullPagePluginInstance(const char *aMimeType,
-                                                         nsIURI* aURI,
-                                                         nsObjectLoadingContent *aContent,
-                                                         nsPluginInstanceOwner **aOwner,
-                                                         nsIStreamListener **aStreamListener)
-{
-#ifdef PLUGIN_LOGGING
-  nsAutoCString urlSpec;
-  aURI->GetSpec(urlSpec);
-  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
-  ("nsPluginHost::InstantiateFullPagePlugin Begin mime=%s, url=%s\n",
-  aMimeType, urlSpec.get()));
-#endif
-
-  nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
-  if (!instanceOwner) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
-  nsresult rv = instanceOwner->Init(ourContent);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = SetUpPluginInstance(aMimeType, aURI, instanceOwner);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsRefPtr<nsNPAPIPluginInstance> instance;
-  instanceOwner->GetInstance(getter_AddRefs(instance));
-  if (!instance) {
-    return NS_ERROR_FAILURE;
-  }
-
-  NPWindow* win = nullptr;
-  instanceOwner->GetWindow(win);
-  if (!win) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Set up any widget that might be required.
-  instanceOwner->CreateWidget();
-  instanceOwner->CallSetWindow();
-
-  rv = NewFullPagePluginStreamListener(aURI, instance.get(), aStreamListener);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // Call SetWindow again in case something changed.
-  instanceOwner->CallSetWindow();
-
-  instanceOwner.forget(aOwner);
-
-  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
-  ("nsPluginHost::InstantiateFullPagePlugin End mime=%s, rv=%d, url=%s\n",
-  aMimeType, rv, urlSpec.get()));
-
-  return NS_OK;
-}
-
 nsPluginTag*
 nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
 {
   nsPluginTag* pluginTag;
   for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
     if (pluginTag->mLibrary == aLibrary) {
       return pluginTag;
     }
@@ -1192,45 +1063,22 @@ nsPluginHost::TrySetUpPluginInstance(con
   if (aURL != nullptr) aURL->GetSpec(urlSpec);
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
         ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
         aMimeType, aOwner, urlSpec.get()));
 
   PR_LogFlush();
 #endif
-  
-  const char* mimetype = nullptr;
-
-  // if don't have a mimetype or no plugin can handle this mimetype
-  // check by file extension
+
   nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
-  if (!pluginTag) {
-    nsCOMPtr<nsIURL> url = do_QueryInterface(aURL);
-    if (!url) return NS_ERROR_FAILURE;
-
-    nsAutoCString fileExtension;
-    url->GetFileExtension(fileExtension);
-
-    // if we don't have an extension or no plugin for this extension,
-    // return failure as there is nothing more we can do
-    if (fileExtension.IsEmpty() ||
-        !(pluginTag = FindPluginEnabledForExtension(fileExtension.get(),
-                                                    mimetype))) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-  else {
-    mimetype = aMimeType;
-  }
-
   NS_ASSERTION(pluginTag, "Must have plugin tag here!");
 
   nsRefPtr<nsNPAPIPlugin> plugin;
-  GetPlugin(mimetype, getter_AddRefs(plugin));
+  GetPlugin(aMimeType, getter_AddRefs(plugin));
   if (!plugin) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
 
   // This will create the owning reference. The connection must be made between the
   // instance and the instance owner before initialization. Plugins can call into
@@ -1239,17 +1087,17 @@ nsPluginHost::TrySetUpPluginInstance(con
 
   // Add the instance to the instances list before we call NPP_New so that
   // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
   mInstances.AppendElement(instance.get());
 
   // this should not addref the instance or owner
   // except in some cases not Java, see bug 140931
   // our COM pointer will free the peer
-  nsresult rv = instance->Initialize(plugin.get(), aOwner, mimetype);
+  nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
   if (NS_FAILED(rv)) {
     mInstances.RemoveElement(instance.get());
     aOwner->SetInstance(nullptr);
     return rv;
   }
 
   // Cancel the plugin unload timer since we are creating
   // an instance for it.
@@ -1289,17 +1137,17 @@ nsPluginHost::IsPluginEnabledForType(con
     if (plugin->HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED))
       return NS_ERROR_PLUGIN_BLOCKLISTED;
     else
       return NS_ERROR_PLUGIN_DISABLED;
   }
 
   return NS_OK;
 }
- 
+
 NS_IMETHODIMP
 nsPluginHost::IsPluginClickToPlayForType(const nsACString &aMimeType, bool *aResult)
 {
   nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
   if (!plugin) {
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -1326,17 +1174,17 @@ nsPluginHost::IsPluginPlayPreviewForType
     nsCString mt = mPlayPreviewMimeTypes[i];
     if (PL_strcasecmp(mt.get(), aMimeType) == 0)
       return true;
   }
   return false;
 }
 
 nsresult
-nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState) 
+nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
 {
   nsPluginTag *plugin = FindPluginForType(aMimeType, true);
   if (plugin) {
     nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
     if (blocklist) {
       // The EmptyString()s are so we use the currently running application
       // and toolkit versions
       return blocklist->GetPluginBlocklistState(plugin, EmptyString(),
@@ -2004,20 +1852,20 @@ int64_t GetPluginLastModifiedTime(const 
   }
 #else
   localfile->GetLastModifiedTime(&fileModTime);
 #endif
 
   return fileModTime;
 }
 
-struct CompareFilesByTime 
+struct CompareFilesByTime
 {
-  bool 
-  LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const 
+  bool
+  LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
   {
     return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
   }
 
   bool
   Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
   {
     return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
@@ -2078,17 +1926,17 @@ nsresult nsPluginHost::ScanPluginsDirect
     nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
 
     nsString utf16FilePath;
     rv = localfile->GetPath(utf16FilePath);
     if (NS_FAILED(rv))
       continue;
 
     int64_t fileModTime = GetPluginLastModifiedTime(localfile);
-    
+
     // Look for it in our cache
     NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
     nsRefPtr<nsPluginTag> pluginTag;
     RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
 
     bool enabled = true;
     bool seenBefore = false;
     if (pluginTag) {
@@ -2107,17 +1955,17 @@ nsresult nsPluginHost::ScanPluginsDirect
       // we're done.
       if (!aCreatePluginList) {
         if (*aPluginsChanged) {
           return NS_OK;
         }
         continue;
       }
     }
-    
+
     bool isKnownInvalidPlugin = false;
     for (nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
          invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
       // If already marked as invalid, ignore it
       if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
           invalidPlugins->mLastModifiedTime == fileModTime) {
         if (aCreatePluginList) {
           invalidPlugins->mSeen = true;
@@ -2139,26 +1987,26 @@ nsresult nsPluginHost::ScanPluginsDirect
       nsPluginInfo info;
       memset(&info, 0, sizeof(info));
       nsresult res = pluginFile.GetPluginInfo(info, &library);
       // if we don't have mime type don't proceed, this is not a plugin
       if (NS_FAILED(res) || !info.fMimeTypeArray) {
         nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
                                                                          fileModTime);
         pluginFile.FreePluginInfo(info);
-        
+
         if (aCreatePluginList) {
           invalidTag->mSeen = true;
         }
         invalidTag->mNext = mInvalidPlugins;
         if (mInvalidPlugins) {
           mInvalidPlugins->mPrev = invalidTag;
         }
         mInvalidPlugins = invalidTag;
-        
+
         // Mark aPluginsChanged so pluginreg is rewritten
         *aPluginsChanged = true;
         continue;
       }
 
       pluginTag = new nsPluginTag(&info);
       pluginFile.FreePluginInfo(info);
       if (!pluginTag)
@@ -2205,25 +2053,25 @@ nsresult nsPluginHost::ScanPluginsDirect
       }
     }
 
     // do it if we still want it
     if (!seenBefore) {
       // We have a valid new plugin so report that plugins have changed.
       *aPluginsChanged = true;
     }
-    
+
     // Avoid adding different versions of the same plugin if they are running 
     // in-process, otherwise we risk undefined behaviour.
     if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
       if (HaveSamePlugin(pluginTag)) {
         continue;
       }
     }
-    
+
     // Don't add the same plugin again if it hasn't changed
     if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
       if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
         continue;
       }
     }
 
     // If we're not creating a plugin list, simply looking for changes,
@@ -2287,17 +2135,17 @@ nsresult nsPluginHost::ScanPluginsDirect
     while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
       nsCOMPtr<nsISupports> supports;
       nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
       if (NS_FAILED(rv))
         continue;
       nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
       if (NS_FAILED(rv))
         continue;
-      
+
       // don't pass aPluginsChanged directly to prevent it from been reset
       bool pluginschanged = false;
       ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
 
       if (pluginschanged)
         *aPluginsChanged = true;
 
       // if changes are detected and we are not creating the list, do not proceed
@@ -2436,17 +2284,17 @@ nsresult nsPluginHost::FindPlugins(bool 
 
   for (uint32_t i = 0; i < size; i+=1) {
     nsCOMPtr<nsIFile> dirToScan;
     bool bExists;
     if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
         dirToScan &&
         NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
         bExists) {
-      
+
       ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
 
       if (pluginschanged)
         *aPluginsChanged = true;
 
       // if we are just looking for possible changes,
       // no need to proceed if changes are detected
       if (!aCreatePluginList && *aPluginsChanged) {
@@ -2466,29 +2314,29 @@ nsresult nsPluginHost::FindPlugins(bool 
     *aPluginsChanged = true;
   }
 
   // Remove unseen invalid plugins
   nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
   while (invalidPlugins) {
     if (!invalidPlugins->mSeen) {
       nsRefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
-      
+
       if (invalidPlugin->mPrev) {
         invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
       }
       else {
         mInvalidPlugins = invalidPlugin->mNext;
       }
       if (invalidPlugin->mNext) {
-        invalidPlugin->mNext->mPrev = invalidPlugin->mPrev; 
+        invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
       }
-      
+
       invalidPlugins = invalidPlugin->mNext;
-      
+
       invalidPlugin->mPrev = NULL;
       invalidPlugin->mNext = NULL;
     }
     else {
       invalidPlugins->mSeen = false;
       invalidPlugins = invalidPlugins->mNext;
     }
   }
@@ -2641,17 +2489,17 @@ nsPluginHost::WritePluginInfo()
   rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
   if (NS_FAILED(rv))
     return rv;
 
   nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
   if (!runtime) {
     return NS_ERROR_FAILURE;
   }
-    
+
   nsAutoCString arch;
   rv = runtime->GetXPCOMABI(arch);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   PR_fprintf(fd, "Generated File. Do not edit.\n");
 
@@ -2713,31 +2561,31 @@ nsPluginHost::WritePluginInfo()
         PLUGIN_REGISTRY_FIELD_DELIMITER,
         (tag->mExtensions[i].get()),
         PLUGIN_REGISTRY_FIELD_DELIMITER,
         PLUGIN_REGISTRY_END_OF_LINE_MARKER);
     }
   }
 
   PR_fprintf(fd, "\n[INVALID]\n");
-  
+
   nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
   while (invalidPlugins) {
     // fullPath
     PR_fprintf(fd, "%s%c%c\n",
       (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
       PLUGIN_REGISTRY_FIELD_DELIMITER,
       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
 
     // lastModifiedTimeStamp
     PR_fprintf(fd, "%lld%c%c\n",
       invalidPlugins->mLastModifiedTime,
       PLUGIN_REGISTRY_FIELD_DELIMITER,
       PLUGIN_REGISTRY_END_OF_LINE_MARKER);
-    
+
     invalidPlugins = invalidPlugins->mNext;
   }
 
   PR_Close(fd);
   nsCOMPtr<nsIFile> parent;
   rv = pluginReg->GetParent(getter_AddRefs(parent));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
@@ -2836,68 +2684,68 @@ nsPluginHost::ReadPluginInfo()
     return rv;
 
   // Registry v0.10 and upwards includes the plugin version field
   bool regHasVersion = (version >= "0.10");
 
   // Registry v0.13 and upwards includes the architecture
   if (version >= "0.13") {
     char* archValues[6];
-    
+
     if (!reader.NextLine()) {
       return rv;
     }
-    
+
     // ArchLiteral, Architecture
     if (2 != reader.ParseLine(archValues, 2)) {
       return rv;
     }
-      
+
     // ArchLiteral
     if (PL_strcmp(archValues[0], "Arch")) {
       return rv;
     }
-      
+
     nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
     if (!runtime) {
       return rv;
     }
-      
+
     nsAutoCString arch;
     if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
       return rv;
     }
-      
+
     // If this is a registry from a different architecture then don't attempt to read it
     if (PL_strcmp(archValues[1], arch.get())) {
       return rv;
     }
   }
-  
+
   // Registry v0.13 and upwards includes the list of invalid plugins
   bool hasInvalidPlugins = (version >= "0.13");
 
   if (!ReadSectionHeader(reader, "PLUGINS"))
     return rv;
 
 #if defined(XP_MACOSX)
   bool hasFullPathInFileNameField = false;
 #else
   bool hasFullPathInFileNameField = (version < "0.11");
 #endif
 
   while (reader.NextLine()) {
     const char *filename;
     const char *fullpath;
     nsAutoCString derivedFileName;
-    
+
     if (hasInvalidPlugins && *reader.LinePtr() == '[') {
       break;
     }
-    
+
     if (hasFullPathInFileNameField) {
       fullpath = reader.LinePtr();
       if (!reader.NextLine())
         return rv;
       // try to derive a file name from the full path
       if (fullpath) {
         nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
         file->InitWithNativePath(nsDependentCString(fullpath));
@@ -3003,33 +2851,33 @@ nsPluginHost::ReadPluginInfo()
 
     // Mark plugin as loaded from cache
     tag->Mark(tagflag | NS_PLUGIN_FLAG_FROMCACHE);
     PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
       ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
     tag->mNext = mCachedPlugins;
     mCachedPlugins = tag;
   }
-  
+
   if (hasInvalidPlugins) {
     if (!ReadSectionHeader(reader, "INVALID")) {
       return rv;
     }
 
     while (reader.NextLine()) {
       const char *fullpath = reader.LinePtr();
       if (!reader.NextLine()) {
         return rv;
       }
-      
+
       const char *lastModifiedTimeStamp = reader.LinePtr();
       int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
-      
+
       nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
-      
+
       invalidTag->mNext = mInvalidPlugins;
       if (mInvalidPlugins) {
         mInvalidPlugins->mPrev = invalidTag;
       }
       mInvalidPlugins = invalidTag;
     }
   }
   return NS_OK;
@@ -3178,17 +3026,17 @@ nsresult nsPluginHost::NewPluginURLStrea
           return NS_ERROR_FAILURE;
         }
         referer = doc->GetDocumentURI();
       }
 
       rv = httpChannel->SetReferrer(referer);
       NS_ENSURE_SUCCESS(rv,rv);
     }
-      
+
     if (aPostStream) {
       // XXX it's a bit of a hack to rewind the postdata stream
       // here but it has to be done in case the post data is
       // being reused multiple times.
       nsCOMPtr<nsISeekableStream>
       postDataSeekable(do_QueryInterface(aPostStream));
       if (postDataSeekable)
         postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
@@ -3341,79 +3189,25 @@ nsPluginHost::StopPluginInstance(nsNPAPI
     aInstance->Destroy();
     mInstances.RemoveElement(aInstance);
     OnPluginInstanceDestroyed(pluginTag);
   }
 
   return NS_OK;
 }
 
-nsresult nsPluginHost::NewEmbeddedPluginStreamListener(nsIURI* aURI,
-                                                       nsObjectLoadingContent *aContent,
-                                                       nsNPAPIPluginInstance* aInstance,
-                                                       nsIStreamListener **aStreamListener)
+nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
+                                               nsNPAPIPluginInstance* aInstance,
+                                               nsIStreamListener **aStreamListener)
 {
   NS_ENSURE_ARG_POINTER(aURI);
   NS_ENSURE_ARG_POINTER(aStreamListener);
 
   nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
-  nsresult rv = listener->InitializeEmbedded(aURI, aInstance, aContent);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  listener.forget(aStreamListener);
-
-  return NS_OK;
-}
-
-nsresult nsPluginHost::NewEmbeddedPluginStream(nsIURI* aURL,
-                                               nsObjectLoadingContent *aContent,
-                                               nsNPAPIPluginInstance* aInstance)
-{
-  nsCOMPtr<nsIStreamListener> listener;
-  nsresult rv = NewEmbeddedPluginStreamListener(aURL, aContent, aInstance,
-                                                getter_AddRefs(listener));
-  if (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsIDocument> doc;
-    nsCOMPtr<nsILoadGroup> loadGroup;
-    if (aContent) {
-      nsCOMPtr<nsIContent> aIContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
-      doc = aIContent->GetDocument();
-      if (doc) {
-        loadGroup = doc->GetDocumentLoadGroup();
-      }
-    }
-    nsCOMPtr<nsIChannel> channel;
-    rv = NS_NewChannel(getter_AddRefs(channel), aURL, nullptr, loadGroup, nullptr);
-    if (NS_SUCCEEDED(rv)) {
-      // if this is http channel, set referrer, some servers are configured
-      // to reject requests without referrer set, see bug 157796
-      nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
-      if (httpChannel && doc)
-        httpChannel->SetReferrer(doc->GetDocumentURI());
-
-      rv = channel->AsyncOpen(listener, nullptr);
-      if (NS_SUCCEEDED(rv))
-        return NS_OK;
-    }
-  }
-
-  return rv;
-}
-
-nsresult nsPluginHost::NewFullPagePluginStreamListener(nsIURI* aURI,
-                                                       nsNPAPIPluginInstance *aInstance,
-                                                       nsIStreamListener **aStreamListener)
-{
-  NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aStreamListener);
-
-  nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
-  nsresult rv = listener->InitializeFullPage(aURI, aInstance);
+  nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   listener.forget(aStreamListener);
 
   return NS_OK;
 }
@@ -4019,17 +3813,17 @@ nsPluginHost::StoppedInstanceCount()
 }
 
 nsTArray< nsRefPtr<nsNPAPIPluginInstance> >*
 nsPluginHost::InstanceArray()
 {
   return &mInstances;
 }
 
-void 
+void
 nsPluginHost::DestroyRunningInstances(nsISupportsArray* aReloadDocs, nsPluginTag* aPluginTag)
 {
   for (int32_t i = mInstances.Length(); i > 0; i--) {
     nsNPAPIPluginInstance *instance = mInstances[i - 1];
     if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
       instance->SetWindow(nullptr);
       instance->Stop();
 
@@ -4157,13 +3951,13 @@ PluginDestructionGuard::DelayDestroy(nsN
     static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
 
   while (g != &sListHead) {
     if (g->mInstance == aInstance) {
       g->mDelayedDestroy = true;
 
       return true;
     }
-    g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));    
+    g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
   }
 
   return false;
 }
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -179,46 +179,33 @@ public:
 
   void DestroyRunningInstances(nsISupportsArray* aReloadDocs, nsPluginTag* aPluginTag);
 
   // Return the tag for |aLibrary| if found, nullptr if not.
   nsPluginTag* FindTagForLibrary(PRLibrary* aLibrary);
 
   // The last argument should be false if we already have an in-flight stream
   // and don't need to set up a new stream.
-  nsresult InstantiateEmbeddedPluginInstance(const char *aMimeType, nsIURI* aURL,
-                                             nsObjectLoadingContent *aContent,
-                                             nsPluginInstanceOwner** aOwner);
-
-  nsresult InstantiateFullPagePluginInstance(const char *aMimeType,
-                                             nsIURI* aURI,
-                                             nsObjectLoadingContent *aContent,
-                                             nsPluginInstanceOwner **aOwner,
-                                             nsIStreamListener **aStreamListener);
+  nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
+                                     nsObjectLoadingContent *aContent,
+                                     nsPluginInstanceOwner** aOwner);
 
   // Does not accept NULL and should never fail.
   nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
 
   nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
 
-  nsresult NewEmbeddedPluginStreamListener(nsIURI* aURL, nsObjectLoadingContent *aContent,
-                                           nsNPAPIPluginInstance* aInstance,
-                                           nsIStreamListener **aStreamListener);
-
-  nsresult NewFullPagePluginStreamListener(nsIURI* aURI,
-                                           nsNPAPIPluginInstance *aInstance,
-                                           nsIStreamListener **aStreamListener);
+  nsresult NewPluginStreamListener(nsIURI* aURL,
+                                   nsNPAPIPluginInstance* aInstance,
+                                   nsIStreamListener **aStreamListener);
 
 private:
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
 
-  nsresult
-  NewEmbeddedPluginStream(nsIURI* aURL, nsObjectLoadingContent *aContent, nsNPAPIPluginInstance* aInstance);
-
   nsPluginTag*
   FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
 
   // Return an nsPluginTag for this type, if any.  If aCheckEnabled is
   // true, only enabled plugins will be returned.
   nsPluginTag*
   FindPluginForType(const char* aMimeType, bool aCheckEnabled);
 
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -22,17 +22,17 @@
 #include "nsPrintfCString.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDocument.h"
 #include "nsIWebNavigation.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsPluginNativeWindow.h"
 #include "sampler.h"
-#include "nsObjectLoadingContent.h"
+#include "nsPluginInstanceOwner.h"
 
 #define MAGIC_REQUEST_CONTEXT 0x01020304
 
 // nsPluginByteRangeStreamListener
 
 class nsPluginByteRangeStreamListener
   : public nsIStreamListener
   , public nsIInterfaceRequestor
@@ -309,101 +309,57 @@ nsPluginStreamListenerPeer::~nsPluginStr
     mFileCacheOutputStream = nullptr;
   
   delete mDataForwardToRequest;
 
   if (mPluginInstance)
     mPluginInstance->FileCachedStreamListeners()->RemoveElement(this);
 }
 
-// Called as a result of GetURL and PostURL
+// Called as a result of GetURL and PostURL, or by the host in the case of the
+// initial plugin stream.
 nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
                                                 nsNPAPIPluginInstance *aInstance,
                                                 nsNPAPIPluginStreamListener* aListener)
 {
 #ifdef PLUGIN_LOGGING
   nsAutoCString urlSpec;
-  if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec);
-  
+  if (aURL != nullptr) aURL->GetSpec(urlSpec);
+
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
          ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
-  
-  PR_LogFlush();
-#endif
-  
-  mURL = aURL;
-  
-  mPluginInstance = aInstance;
 
-  mPStreamListener = aListener;
-  mPStreamListener->SetStreamListenerPeer(this);
-
-  mPendingRequests = 1;
-  
-  mDataForwardToRequest = new nsHashtable(16, false);
-  if (!mDataForwardToRequest)
-    return NS_ERROR_FAILURE;
-  
-  return NS_OK;
-}
-
-nsresult nsPluginStreamListenerPeer::InitializeEmbedded(nsIURI *aURL,
-                                                        nsNPAPIPluginInstance* aInstance,
-                                                        nsObjectLoadingContent *aContent)
-{
-#ifdef PLUGIN_LOGGING
-  nsAutoCString urlSpec;
-  aURL->GetSpec(urlSpec);
-  
-  PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
-         ("nsPluginStreamListenerPeer::InitializeEmbedded url=%s\n", urlSpec.get()));
-  
   PR_LogFlush();
 #endif
 
-  // We have to have one or the other.
-  if (!aInstance && !aContent) {
+  // Not gonna work out
+  if (!aInstance) {
     return NS_ERROR_FAILURE;
   }
 
   mURL = aURL;
-  
-  if (aInstance) {
-    NS_ASSERTION(mPluginInstance == nullptr, "nsPluginStreamListenerPeer::InitializeEmbedded mPluginInstance != nullptr");
-    mPluginInstance = aInstance;
-  } else {
-    mContent = aContent;
+
+  NS_ASSERTION(mPluginInstance == nullptr,
+               "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr");
+  mPluginInstance = aInstance;
+
+  // If the plugin did not request this stream, e.g. the initial stream, we wont
+  // have a nsNPAPIPluginStreamListener yet - this will be handled by
+  // SetUpStreamListener
+  if (aListener) {
+    mPStreamListener = aListener;
+    mPStreamListener->SetStreamListenerPeer(this);
   }
-  
+
   mPendingRequests = 1;
-  
-  mDataForwardToRequest = new nsHashtable(16, false);
-  if (!mDataForwardToRequest)
-    return NS_ERROR_FAILURE;
-  
-  return NS_OK;
-}
 
-// Called by NewFullPagePluginStream()
-nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance)
-{
-  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
-             ("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
-  
-  NS_ASSERTION(mPluginInstance == nullptr, "nsPluginStreamListenerPeer::InitializeFullPage mPluginInstance != nullptr");
-  mPluginInstance = aInstance;
-  
-  mURL = aURL;
-  
   mDataForwardToRequest = new nsHashtable(16, false);
   if (!mDataForwardToRequest)
     return NS_ERROR_FAILURE;
 
-  mPendingRequests = 1;
-  
   return NS_OK;
 }
 
 // SetupPluginCacheFile is called if we have to save the stream to disk.
 // the most likely cause for this is either there is no disk cache available
 // or the stream is coming from a https server.
 //
 // These files will be deleted when the host is destroyed.
@@ -492,41 +448,41 @@ nsPluginStreamListenerPeer::OnStartReque
   nsresult rv = NS_OK;
   SAMPLE_LABEL("nsPluginStreamListenerPeer", "OnStartRequest");
 
   if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) {
     NS_ASSERTION(mRequests.Count() == 0,
                  "Only our initial stream should be unknown!");
     TrackRequest(request);
   }
-  
+
   if (mHaveFiredOnStartRequest) {
     return NS_OK;
   }
-  
+
   mHaveFiredOnStartRequest = true;
-  
+
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
-  
+
   // deal with 404 (Not Found) HTTP response,
   // just return, this causes the request to be ignored.
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     uint32_t responseCode = 0;
     rv = httpChannel->GetResponseStatus(&responseCode);
     if (NS_FAILED(rv)) {
       // NPP_Notify() will be called from OnStopRequest
       // in nsNPAPIPluginStreamListener::CleanUpStream
       // return error will cancel this request
       // ...and we also need to tell the plugin that
       mRequestFailed = true;
       return NS_ERROR_FAILURE;
     }
-    
+
     if (responseCode > 206) { // not normal
       bool bWantsAllNetworkStreams = false;
 
       // We don't always have an instance here already, but if we do, check
       // to see if it wants all streams.
       if (mPluginInstance) {
         rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
                                                  &bWantsAllNetworkStreams);
@@ -537,90 +493,76 @@ nsPluginStreamListenerPeer::OnStartReque
       }
 
       if (!bWantsAllNetworkStreams) {
         mRequestFailed = true;
         return NS_ERROR_FAILURE;
       }
     }
   }
-  
+
   // Get the notification callbacks from the channel and save it as
   // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
   // we'll create channel for byte range request.
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
   if (callbacks)
     mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
-  
+
   nsCOMPtr<nsILoadGroup> loadGroup;
   channel->GetLoadGroup(getter_AddRefs(loadGroup));
   if (loadGroup)
     mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
-  
+
   int64_t length;
   rv = channel->GetContentLength(&length);
-  
+
   // it's possible for the server to not send a Content-Length.
   // we should still work in this case.
   if (NS_FAILED(rv) || length == -1) {
     // check out if this is file channel
     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
     if (fileChannel) {
       // file does not exist
       mRequestFailed = true;
       return NS_ERROR_FAILURE;
     }
     mLength = 0;
   }
   else {
     mLength = length;
   }
-  
+
   nsAutoCString aContentType; // XXX but we already got the type above!
   rv = channel->GetContentType(aContentType);
   if (NS_FAILED(rv))
     return rv;
-  
+
   nsCOMPtr<nsIURI> aURL;
   rv = channel->GetURI(getter_AddRefs(aURL));
   if (NS_FAILED(rv))
     return rv;
-  
+
   aURL->GetSpec(mURLSpec);
-  
+
   if (!aContentType.IsEmpty())
     mContentType = aContentType;
-  
+
 #ifdef PLUGIN_LOGGING
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
          ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
           this, request, aContentType.get(), mURLSpec.get()));
-  
+
   PR_LogFlush();
 #endif
 
-  // If we don't have an instance yet it means we weren't able to load
-  // a plugin previously because we didn't have the mimetype. Try again
-  // if we have a mime type now.
-  if (!mPluginInstance && mContent && !aContentType.IsEmpty()) {
-    nsObjectLoadingContent *olc = static_cast<nsObjectLoadingContent*>(mContent.get());
-    rv = olc->InstantiatePluginInstance();
-    if (NS_SUCCEEDED(rv)) {
-      rv = olc->GetPluginInstance(getter_AddRefs(mPluginInstance));
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
-    }
-  }
-
   // Set up the stream listener...
   rv = SetUpStreamListener(request, aURL);
   if (NS_FAILED(rv)) return rv;
-  
+
   return rv;
 }
 
 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
                                                      nsISupports* aContext,
                                                      uint64_t aProgress,
                                                      uint64_t aProgressMax)
 {
--- a/dom/plugins/base/nsPluginStreamListenerPeer.h
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.h
@@ -12,20 +12,18 @@
 #include "nsIProgressEventSink.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsWeakReference.h"
 #include "nsNPAPIPluginStreamListener.h"
 #include "nsHashtable.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
-#include "nsIObjectLoadingContent.h"
 
 class nsIChannel;
-class nsObjectLoadingContent;
 
 /**
  * When a plugin requests opens multiple requests to the same URL and
  * the request must be satified by saving a file to disk, each stream
  * listener holds a reference to the backing file: the file is only removed
  * when all the listeners are done.
  */
 class CachedFileHolder
@@ -61,36 +59,31 @@ public:
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIHTTPHEADERVISITOR
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
 
   // Called by RequestRead
   void
   MakeByteRangeString(NPByteRange* aRangeList, nsACString &string, int32_t *numRequests);
-  
+
   bool UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi);
-  
-  // Called by GetURL and PostURL (via NewStream)
+
+  // Called by GetURL and PostURL (via NewStream) or by the host in the case of
+  // the initial plugin stream.
   nsresult Initialize(nsIURI *aURL,
                       nsNPAPIPluginInstance *aInstance,
                       nsNPAPIPluginStreamListener *aListener);
-  
-  nsresult InitializeEmbedded(nsIURI *aURL,
-                              nsNPAPIPluginInstance* aInstance,
-                              nsObjectLoadingContent *aContent);
-  
-  nsresult InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance);
 
   nsresult OnFileAvailable(nsIFile* aFile);
-  
+
   nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
-  
+
   nsNPAPIPluginInstance *GetPluginInstance() { return mPluginInstance; }
-  
+
   nsresult RequestRead(NPByteRange* rangeList);
   nsresult GetLength(uint32_t* result);
   nsresult GetURL(const char** result);
   nsresult GetLastModified(uint32_t* result);
   nsresult IsSeekable(bool* result);
   nsresult GetContentType(char** result);
   nsresult GetStreamOffset(int32_t* result);
   nsresult SetStreamOffset(int32_t value);
@@ -135,17 +128,16 @@ public:
 
 private:
   nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
   nsresult SetupPluginCacheFile(nsIChannel* channel);
   nsresult GetInterfaceGlobal(const nsIID& aIID, void** result);
 
   nsCOMPtr<nsIURI> mURL;
   nsCString mURLSpec; // Have to keep this member because GetURL hands out char*
-  nsCOMPtr<nsIObjectLoadingContent> mContent;
   nsRefPtr<nsNPAPIPluginStreamListener> mPStreamListener;
 
   // Set to true if we request failed (like with a HTTP response of 404)
   bool                    mRequestFailed;
   
   /*
    * Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
    * been called.  Checked in ::OnStopRequest so we can call the
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -160,33 +160,43 @@ PluginModuleParent::WriteExtraDataForMin
 
     // Get the plugin filename, try to get just the file leafname
     const std::string& pluginFile = mSubprocess->GetPluginFilePath();
     size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
     if (filePos == std::string::npos)
         filePos = 0;
     else
         filePos++;
-    notes.Put(CS("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
+    notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
+
+    nsCString pluginName;
+    nsCString pluginVersion;
 
-    //TODO: add plugin name and version: bug 539841
-    // (as PluginName, PluginVersion)
-    notes.Put(CS("PluginName"), CS(""));
-    notes.Put(CS("PluginVersion"), CS(""));
+    nsRefPtr<nsPluginHost> ph = already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+    if (ph) {
+        nsPluginTag* tag = ph->TagForPlugin(mPlugin);
+        if (tag) {
+            pluginName = tag->mName;
+            pluginVersion = tag->mVersion;
+        }
+    }
+        
+    notes.Put(NS_LITERAL_CSTRING("PluginName"), pluginName);
+    notes.Put(NS_LITERAL_CSTRING("PluginVersion"), pluginVersion);
 
     CrashReporterParent* crashReporter = CrashReporter();
     if (crashReporter) {
 #ifdef XP_WIN
         if (mPluginCpuUsageOnHang.Length() > 0) {
-            notes.Put(CS("NumberOfProcessors"),
+            notes.Put(NS_LITERAL_CSTRING("NumberOfProcessors"),
                       nsPrintfCString("%d", PR_GetNumberOfProcessors()));
 
             nsCString cpuUsageStr;
             cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100);
-            notes.Put(CS("PluginCpuUsage"), cpuUsageStr);
+            notes.Put(NS_LITERAL_CSTRING("PluginCpuUsage"), cpuUsageStr);
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
             for (uint32_t i=1; i<mPluginCpuUsageOnHang.Length(); ++i) {
                 nsCString tempStr;
                 tempStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[i] * 100) / 100);
                 notes.Put(nsPrintfCString("CpuUsageFlashProcess%d", i), tempStr);
             }
 #endif
--- a/dom/plugins/test/testplugin/nptest_platform.h
+++ b/dom/plugins/test/testplugin/nptest_platform.h
@@ -51,17 +51,17 @@ bool    pluginSupportsWindowlessMode();
 /**
  * Returns true if the plugin supports async bitmap drawing.
  */
 bool    pluginSupportsAsyncBitmapDrawing();
 
 /**
  * Returns true if the plugin supports DXGI bitmap drawing.
  */
-static bool    pluginSupportsAsyncDXGIDrawing()
+inline bool    pluginSupportsAsyncDXGIDrawing()
 {
 #ifdef XP_WIN
   return true;
 #else
   return false;
 #endif
 }
 
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -49,17 +49,20 @@ SettingsLock.prototype = {
         case "clear":
           let req = store.clear();
           req.onsuccess = function() { this._open = true;
                                        Services.DOMRequest.fireSuccess(request, 0);
                                        this._open = false; }.bind(lock);
           req.onerror = function() { Services.DOMRequest.fireError(request, 0) };
           break;
         case "set":
-          for (let key in info.settings) {
+          let keys = Object.getOwnPropertyNames(info.settings);
+          for (let i = 0; i < keys.length; i++) {
+            let key = keys[i];
+            let last = i === keys.length - 1;
             debug("key: " + key + ", val: " + JSON.stringify(info.settings[key]) + ", type: " + typeof(info.settings[key]));
 
             let checkKeyRequest = store.get(key);
             checkKeyRequest.onsuccess = function (event) {
               if (!event.target.result) {
                 debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database. Please add it to build/settings.js\n");
               }
             }
@@ -67,25 +70,29 @@ SettingsLock.prototype = {
             if(typeof(info.settings[key]) != 'object') {
               req = store.put({settingName: key, settingValue: info.settings[key]});
             } else {
               //Workaround for cloning issues
               let obj = JSON.parse(JSON.stringify(info.settings[key]));
               req = store.put({settingName: key, settingValue: obj});
             }
 
-            req.onsuccess = function() { 
-              lock._open = true;
-              Services.DOMRequest.fireSuccess(request, 0);
+            req.onsuccess = function() {
+              if (last && !request.error) {
+                lock._open = true;
+                Services.DOMRequest.fireSuccess(request, 0);
+                lock._open = false;
+              }
               cpmm.sendAsyncMessage("Settings:Changed", { key: key, value: info.settings[key] });
-              lock._open = false;
             };
 
             req.onerror = function() {
-              Services.DOMRequest.fireError(request, 0)
+              if (!request.error) {
+                Services.DOMRequest.fireError(request, req.error.name)
+              }
             };
           }
           break;
         case "get":
           req = (info.name === "*") ? store.mozGetAll()
                                     : store.mozGetAll(info.name);
 
           req.onsuccess = function(event) {
--- a/dom/settings/tests/test_settings_basics.html
+++ b/dom/settings/tests/test_settings_basics.html
@@ -38,16 +38,23 @@ function onFailure() {
 var wifi  = {"net3g.apn": "internet.mnc012.mcc345.gprs"};
 var wifi2 = {"net3g.apn": "internet.mnc012.mcc345.test"};
 var wifiEnabled = {"wifi.enabled": true};
 var wifiDisabled = {"wifi.enabled": false};
 var screenBright = {"screen.brightness": 0.7};
 var wifiNetworks0 = { "wifi.networks[0]": { ssid: "myfreenetwork", mac: "01:23:45:67:89:ab", passwd: "secret"}};
 var wifiNetworks1 = { "wifi.networks[1]": { ssid: "myfreenetwork2", mac: "01:23:45:67:89:ab", passwd: "secret2"}};
 
+var combination = {
+  "wifi.enabled": false,
+  "screen.brightness": 0.7,
+  "wifi.networks[0]": { ssid: "myfreenetwork", mac: "01:23:45:67:89:ab", passwd: "secret" },
+  "test.test": true
+}
+
 function equals(o1, o2) {
   var k1 = Object.keys(o1).sort();
   var k2 = Object.keys(o2).sort();
   if (k1.length != k2.length) return false;
   return k1.zip(k2, function(keyPair) {
     if(typeof o1[keyPair[0]] == typeof o2[keyPair[1]] == "object"){
       return equals(o1[keyPair[0]], o2[keyPair[1]])
     } else {
@@ -606,16 +613,46 @@ var steps = [
     var lock = mozSettings.createLock();
     req = lock.clear();
     req.onsuccess = function () {
       ok(true, "Deleted the database");
       next();
     };
     req.onerror = onFailure;
   },
+  function() {
+    ok(true, "Set with multiple arguments");
+    var lock = mozSettings.createLock();
+    req = lock.set(combination);
+    req.onsuccess = function () {
+      ok(true, "Set Done");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "request argument from multiple set");
+    var lock = mozSettings.createLock();
+    req = lock.get("screen.brightness");
+    req.onsuccess = function () {
+      check(req.result["screen.brightness"], 0.7, "get done");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Clear DB");
+    var lock = mozSettings.createLock();
+    req = lock.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    };
+    req.onerror = onFailure;
+  },
   function () {
     ok(true, "all done!\n");
     SimpleTest.finish();
   }
 ];
 
 function next() {
   ok(true, "Begin!");
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -2646,17 +2646,25 @@ RILNetworkInterface.prototype = {
     if (message.apn != this.dataCallSettings["apn"]) {
       return;
     }
     debug("Data call error on APN: " + message.apn);
     this.reset();
   },
 
   dataCallStateChanged: function dataCallStateChanged(datacall) {
-    if (datacall.apn != this.dataCallSettings["apn"]) {
+    if (this.cid && this.cid != datacall.cid) {
+    // If data call for this connection existed but cid mismatched,
+    // it means this datacall state change is not for us.
+      return;
+    }
+    // If data call for this connection does not exist, it could be state
+    // change for new data call.  We only update data call state change
+    // if APN name matched.
+    if (!this.cid && datacall.apn != this.dataCallSettings["apn"]) {
       return;
     }
     debug("Data call ID: " + datacall.cid + ", interface name: " +
           datacall.ifname + ", APN name: " + datacall.apn);
     if (this.connecting &&
         (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTING ||
          datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED)) {
       this.connecting = false;
@@ -2670,17 +2678,20 @@ RILNetworkInterface.prototype = {
         this.dns1 = datacall.dns[0];
         this.dns2 = datacall.dns[1];
       }
       if (!this.registeredAsNetworkInterface) {
         gNetworkManager.registerNetworkInterface(this);
         this.registeredAsNetworkInterface = true;
       }
     }
-    if (this.cid != datacall.cid) {
+    // In current design, we don't update status of secondary APN if it shares
+    // same APN name with the default APN.  In this condition, this.cid will
+    // not be set and we don't want to update its status.
+    if (this.cid == null) {
       return;
     }
     if (this.state == datacall.state) {
       return;
     }
 
     this.state = datacall.state;
 
@@ -2690,16 +2701,17 @@ RILNetworkInterface.prototype = {
     if (this == this.mRIL.dataNetworkInterface) {
       this.mRIL.updateRILNetworkInterface();
     }
 
     if (this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN &&
        this.registeredAsNetworkInterface) {
       gNetworkManager.unregisterNetworkInterface(this);
       this.registeredAsNetworkInterface = false;
+      this.cid = null;
       return;
     }
 
     Services.obs.notifyObservers(this,
                                  kNetworkInterfaceStateChangedTopic,
                                  null);
   },
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -1515,17 +1515,17 @@ let RIL = {
    *
    * See ETSI TS 100.977 section 10.3.4 EF_PLMNsel
    */
   getPLMNSelector: function getPLMNSelector() {
     function callback() {
       if (DEBUG) debug("PLMN Selector: Process PLMN Selector");
 
       let length = Buf.readUint32();
-      this.iccInfoPrivate.PLMN = this.readPLMNEntries(length/6);
+      this.iccInfoPrivate.PLMN = this.readPLMNEntries(length/3);
       Buf.readStringDelimiter(length);
 
       if (DEBUG) debug("PLMN Selector: " + JSON.stringify(this.iccInfoPrivate.PLMN));
 
       if (this.updateDisplayCondition()) {
         this._handleICCInfoChange();
       }
     }
@@ -1565,17 +1565,17 @@ let RIL = {
         tlvLen = GsmPDUHelper.readHexOctet();
         readLen += 2; // For tag and length.
         switch (tlvTag) {
         case SPDI_TAG_SPDI:
           // The value part itself is a TLV.
           continue;
         case SPDI_TAG_PLMN_LIST:
           // This PLMN list is what we want.
-          this.iccInfoPrivate.SPDI = readPLMNEntries(tlvLen/6);
+          this.iccInfoPrivate.SPDI = this.readPLMNEntries(tlvLen/3);
           readLen += tlvLen;
           endLoop = true;
           break;
         default:
           // We don't care about its content if its tag is not SPDI nor
           // PLMN_LIST.
           GsmPDUHelper.readHexOctetArray(tlvLen);
           readLen += tlvLen;
@@ -2312,17 +2312,17 @@ let RIL = {
                     GsmPDUHelper.readHexOctet(),
                     GsmPDUHelper.readHexOctet()];
         if (DEBUG) debug("readPLMNEntries: Reading PLMN entry: [" + index +
                          "]: '" + plmn + "'");
         if (plmn[0] != 0xFF &&
             plmn[1] != 0xFF &&
             plmn[2] != 0xFF) {
           let semiOctets = [];
-          for (let i = 0; i < plmn.length; i++) {
+          for (let idx = 0; idx < plmn.length; idx++) {
             semiOctets.push((plmn[idx] & 0xF0) >> 4);
             semiOctets.push(plmn[idx] & 0x0F);
           }
 
           // According to TS 24.301, 9.9.3.12, the semi octets is arranged
           // in format:
           // Byte 1: MCC[2] | MCC[1]
           // Byte 2: MNC[3] | MCC[3]
@@ -3850,16 +3850,17 @@ let RIL = {
           case ICC_EF_FDN:
           case ICC_EF_MSISDN:
             return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM;
 
           case ICC_EF_AD:
           case ICC_EF_MBDN:
           case ICC_EF_PLMNsel:
           case ICC_EF_SPN:
+          case ICC_EF_SPDI:
           case ICC_EF_SST:
           case ICC_EF_CBMI:
           case ICC_EF_CBMIR:
             return EF_PATH_MF_SIM + EF_PATH_DF_GSM;
         }
       case CARD_APPTYPE_USIM:
         switch (fileId) {
           case ICC_EF_AD:
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -832,8 +832,43 @@ add_test(function test_stk_proactive_com
   ctlvs = berTlv.value;
   tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
   do_check_eq(tlv.value.commandNumber, 0x01);
   do_check_eq(tlv.value.typeOfCommand, STK_CMD_PROVIDE_LOCAL_INFO);
   do_check_eq(tlv.value.commandQualifier, STK_LOCAL_INFO_DATE_TIME_ZONE);
 
   run_next_test();
 });
+
+add_test(function test_path_id_for_spid_and_spn() {
+  let RIL = newWorker({
+    postRILMessage: function fakePostRILMessage(data) {
+      // Do nothing
+    },
+    postMessage: function fakePostMessage(message) {
+      // Do nothing
+    }
+  }).RIL;
+
+  // Test SIM
+  RIL.iccStatus = {
+    gsmUmtsSubscriptionAppIndex: 0,
+    apps: [
+      {
+        app_type: CARD_APPTYPE_SIM
+      }, {
+        app_type: CARD_APPTYPE_USIM
+      }
+    ]
+  }
+  do_check_eq(RIL._getPathIdForICCRecord(ICC_EF_SPDI),
+              EF_PATH_MF_SIM + EF_PATH_DF_GSM);
+  do_check_eq(RIL._getPathIdForICCRecord(ICC_EF_SPN),
+              EF_PATH_MF_SIM + EF_PATH_DF_GSM);
+
+  // Test USIM
+  RIL.iccStatus.gsmUmtsSubscriptionAppIndex = 1;
+  do_check_eq(RIL._getPathIdForICCRecord(ICC_EF_SPDI),
+              EF_PATH_MF_SIM + EF_PATH_ADF_USIM);
+  do_check_eq(RIL._getPathIdForICCRecord(ICC_EF_SPDI),
+              EF_PATH_MF_SIM + EF_PATH_ADF_USIM);
+  run_next_test();
+});
\ No newline at end of file
--- a/dom/time/DateCacheCleaner.cpp
+++ b/dom/time/DateCacheCleaner.cpp
@@ -34,16 +34,17 @@ public:
       do_GetService("@mozilla.org/js/xpc/ContextStack;1");
     if (!stack) {
       NS_WARNING("Failed to get JSContextStack");
     }
     JSContext *cx = stack->GetSafeJSContext();
     if (!cx) {
       NS_WARNING("Failed to GetSafeJSContext");
     }
+    JSAutoRequest ar(cx);
     JS_ClearDateCaches(cx);
   }
 
 };
 
 StaticAutoPtr<DateCacheCleaner> sDateCacheCleaner;
 
 void
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSPrimitiveValue.webidl
@@ -0,0 +1,58 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+interface Counter;
+interface Rect;
+interface RGBColor;
+
+interface CSSPrimitiveValue : CSSValue {
+
+  // UnitTypes
+  const unsigned short      CSS_UNKNOWN                    = 0;
+  const unsigned short      CSS_NUMBER                     = 1;
+  const unsigned short      CSS_PERCENTAGE                 = 2;
+  const unsigned short      CSS_EMS                        = 3;
+  const unsigned short      CSS_EXS                        = 4;
+  const unsigned short      CSS_PX                         = 5;
+  const unsigned short      CSS_CM                         = 6;
+  const unsigned short      CSS_MM                         = 7;
+  const unsigned short      CSS_IN                         = 8;
+  const unsigned short      CSS_PT                         = 9;
+  const unsigned short      CSS_PC                         = 10;
+  const unsigned short      CSS_DEG                        = 11;
+  const unsigned short      CSS_RAD                        = 12;
+  const unsigned short      CSS_GRAD                       = 13;
+  const unsigned short      CSS_MS                         = 14;
+  const unsigned short      CSS_S                          = 15;
+  const unsigned short      CSS_HZ                         = 16;
+  const unsigned short      CSS_KHZ                        = 17;
+  const unsigned short      CSS_DIMENSION                  = 18;
+  const unsigned short      CSS_STRING                     = 19;
+  const unsigned short      CSS_URI                        = 20;
+  const unsigned short      CSS_IDENT                      = 21;
+  const unsigned short      CSS_ATTR                       = 22;
+  const unsigned short      CSS_COUNTER                    = 23;
+  const unsigned short      CSS_RECT                       = 24;
+  const unsigned short      CSS_RGBCOLOR                   = 25;
+
+  readonly attribute unsigned short   primitiveType;
+  [Throws]
+  void               setFloatValue(unsigned short unitType,
+                                   float floatValue);
+  [Throws]
+  float              getFloatValue(unsigned short unitType);
+  [Throws]
+  void               setStringValue(unsigned short stringType,
+                                    DOMString stringValue);
+  [Throws]
+  DOMString          getStringValue();
+  [Throws]
+  Counter            getCounterValue();
+  [Throws]
+  Rect               getRectValue();
+  [Throws]
+  RGBColor           getRGBColorValue();
+};
--- a/dom/webidl/CSSStyleDeclaration.webidl
+++ b/dom/webidl/CSSStyleDeclaration.webidl
@@ -3,30 +3,29 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/csswg/cssom/
  */
 
 interface CSSRule;
-interface CSSValue;
 
 interface CSSStyleDeclaration {
   [SetterThrows]
   attribute DOMString cssText;
 
   readonly attribute unsigned long length;
   getter DOMString item(unsigned long index);
 
   [Throws]
   DOMString getPropertyValue(DOMString property);
   // Mozilla extension, sort of
   [Throws]
-  CSSValue getPropertyCSSValue(DOMString property);
+  CSSValue? getPropertyCSSValue(DOMString property);
   DOMString getPropertyPriority(DOMString property);
   // This would be nicer if it used a string default value of "".
   // See bug 759622.
   [Throws]
   void setProperty(DOMString property, DOMString value, [TreatNullAs=EmptyString] optional DOMString priority);
   [Throws]
   DOMString removeProperty(DOMString property);
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSValue.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+interface CSSValue {
+
+  // UnitTypes
+  const unsigned short      CSS_INHERIT                    = 0;
+  const unsigned short      CSS_PRIMITIVE_VALUE            = 1;
+  const unsigned short      CSS_VALUE_LIST                 = 2;
+  const unsigned short      CSS_CUSTOM                     = 3;
+
+           [Throws]
+           attribute DOMString        cssText;
+
+  readonly attribute unsigned short   cssValueType;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSValueList.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+interface CSSValueList : CSSValue {
+         readonly attribute unsigned long    length;
+  getter CSSValue           item(unsigned long index);
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -16,17 +16,20 @@ webidl_files = \
   AudioListener.webidl \
   AudioNode.webidl \
   AudioParam.webidl \
   AudioSourceNode.webidl \
   BiquadFilterNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
   ClientRectList.webidl \
+  CSSPrimitiveValue.webidl \
   CSSStyleDeclaration.webidl \
+  CSSValue.webidl \
+  CSSValueList.webidl \
   DelayNode.webidl \
   DOMImplementation.webidl \
   DOMParser.webidl \
   DOMSettableTokenList.webidl \
   DOMStringMap.webidl \
   DOMTokenList.webidl \
   DynamicsCompressorNode.webidl \
   Element.webidl \
--- a/editor/libeditor/html/Makefile.in
+++ b/editor/libeditor/html/Makefile.in
@@ -55,8 +55,14 @@ FORCE_STATIC_LIB = 1
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES        += -I$(topsrcdir)/editor/libeditor/base \
                    -I$(topsrcdir)/editor/libeditor/text \
                    -I$(topsrcdir)/editor/txmgr/src \
                    -I$(topsrcdir)/content/base/src \
                    -I$(topsrcdir)/layout/style \
                    $(NULL)
+
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/layout/generic \
+  -I$(topsrcdir)/layout/tables \
+  -I$(topsrcdir)/layout/xul/base/src \
+  $(null)
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -15,17 +15,16 @@
 #include "nsIEditorMailSupport.h"
 #include "nsIEditorStyleSheets.h"
 #include "nsITextServicesDocument.h"
 
 #include "nsEditor.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsICSSLoaderObserver.h"
-#include "nsITableLayout.h"
 
 #include "nsEditRules.h"
 
 #include "nsEditProperty.h"
 #include "nsHTMLCSSUtils.h"
 
 #include "nsHTMLObjectResizer.h"
 #include "nsIHTMLAbsPosEditor.h"
@@ -47,16 +46,17 @@
 class nsIDOMKeyEvent;
 class nsITransferable;
 class nsIDocumentEncoder;
 class nsIClipboard;
 class TypeInState;
 class nsIContentFilter;
 class nsIURL;
 class nsILinkHandler;
+class nsTableOuterFrame;
 struct PropItem;
 
 namespace mozilla {
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
@@ -436,18 +436,18 @@ protected:
 
   // Move all contents from aCellToMerge into aTargetCell (append at end)
   NS_IMETHOD MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell, nsCOMPtr<nsIDOMElement> aCellToMerge, bool aDeleteCellToMerge);
 
   NS_IMETHOD DeleteTable2(nsIDOMElement *aTable, nsISelection *aSelection);
   NS_IMETHOD SetColSpan(nsIDOMElement *aCell, int32_t aColSpan);
   NS_IMETHOD SetRowSpan(nsIDOMElement *aCell, int32_t aRowSpan);
 
-  // Helper used to get nsITableLayout interface for methods implemented in nsTableFrame
-  NS_IMETHOD GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject);
+  // Helper used to get nsTableOuterFrame for a table.
+  nsTableOuterFrame* GetTableFrame(nsIDOMElement* aTable);
   // Needed to do appropriate deleting when last cell or row is about to be deleted
   // This doesn't count cells that don't start in the given row (are spanning from row above)
   int32_t  GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex);
   // Test if all cells in row or column at given index are selected
   bool AllCellsInRowSelected(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aNumberOfColumns);
   bool AllCellsInColumnSelected(nsIDOMElement *aTable, int32_t aColIndex, int32_t aNumberOfRows);
 
   bool IsEmptyCell(mozilla::dom::Element* aCell);
--- a/editor/libeditor/html/nsTableEditor.cpp
+++ b/editor/libeditor/html/nsTableEditor.cpp
@@ -27,21 +27,22 @@
 #include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsIHTMLEditor.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISupportsUtils.h"
 #include "nsITableCellLayout.h" // For efficient access to table cell
 #include "nsITableEditor.h"
-#include "nsITableLayout.h"     //  data owned by the table and cell frames
 #include "nsLiteralString.h"
 #include "nsQueryFrame.h"
 #include "nsString.h"
 #include "nsTArray.h"
+#include "nsTableCellFrame.h"
+#include "nsTableOuterFrame.h"
 #include "nscore.h"
 
 using namespace mozilla;
 
 /***************************************************************************
  * stack based helper class for restoring selection after table edit
  */
 class NS_STACK_CLASS nsSetSelectionAfterTableEdit
@@ -2602,33 +2603,24 @@ nsHTMLEditor::GetCellIndexes(nsIDOMEleme
   nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame();
   NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE);
 
   nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject);
   NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE);
   return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex);
 }
 
-NS_IMETHODIMP
-nsHTMLEditor::GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **tableLayoutObject)
+nsTableOuterFrame*
+nsHTMLEditor::GetTableFrame(nsIDOMElement* aTable)
 {
-  *tableLayoutObject = nullptr;
-  NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED);
-  NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
-  nsCOMPtr<nsIPresShell> ps = GetPresShell();
-  NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
+  NS_ENSURE_TRUE(aTable, nullptr);
 
   nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) );
-  NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE);
-  // frames are not ref counted, so don't use an nsCOMPtr
-  nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame();
-  NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE);
-
-  *tableLayoutObject = do_QueryFrame(layoutObject);
-  return *tableLayoutObject ? NS_OK : NS_NOINTERFACE;
+  NS_ENSURE_TRUE(nodeAsContent, nullptr);
+  return do_QueryFrame(nodeAsContent->GetPrimaryFrame());
 }
 
 //Return actual number of cells (a cell with colspan > 1 counts as just 1)
 int32_t nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex)
 {
   int32_t cellCount = 0;
   nsCOMPtr<nsIDOMElement> cell;
   int32_t colIndex = 0;
@@ -2670,23 +2662,23 @@ nsHTMLEditor::GetTableSize(nsIDOMElement
   *aRowCount = 0;
   *aColCount = 0;
   nsCOMPtr<nsIDOMElement> table;
   // Get the selected talbe or the table enclosing the selection anchor
   res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable, getter_AddRefs(table));
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
   
-  // frames are not ref counted, so don't use an nsCOMPtr
-  nsITableLayout *tableLayoutObject;
-  res = GetTableLayoutObject(table.get(), &tableLayoutObject);
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(tableLayoutObject, NS_ERROR_FAILURE);
-
-  return tableLayoutObject->GetTableSize(*aRowCount, *aColCount); 
+  nsTableOuterFrame* tableFrame = GetTableFrame(table.get());
+  NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
+
+  *aRowCount = tableFrame->GetRowCount();
+  *aColCount = tableFrame->GetColCount();
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, int32_t aRowIndex,
                             int32_t aColIndex, nsIDOMElement **aCell, 
                             int32_t* aStartRowIndex, int32_t* aStartColIndex, 
                             int32_t* aRowSpan, int32_t* aColSpan, 
                             int32_t* aActualRowSpan, int32_t* aActualColSpan, 
@@ -2719,63 +2711,68 @@ nsHTMLEditor::GetCellDataAt(nsIDOMElemen
     res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table));
     NS_ENSURE_SUCCESS(res, res);
     if (table)
       aTable = table;
     else
       return NS_ERROR_FAILURE;
   }
   
-  // frames are not ref counted, so don't use an nsCOMPtr
-  nsITableLayout *tableLayoutObject;
-  res = GetTableLayoutObject(aTable, &tableLayoutObject);
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(tableLayoutObject, NS_ERROR_FAILURE);
-
-  // Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND when
-  //  the index(es) are out of bounds
-  nsCOMPtr<nsIDOMElement> cell;
-  res = tableLayoutObject->GetCellDataAt(aRowIndex, aColIndex,
-                                         *getter_AddRefs(cell), 
-                                         *aStartRowIndex, *aStartColIndex,
-                                         *aRowSpan, *aColSpan, 
-                                         *aActualRowSpan, *aActualColSpan, 
-                                         *aIsSelected);
-  if (cell)
-  {
-    *aCell = cell.get();
-    NS_ADDREF(*aCell);
-  }
-  // Convert to editor's generic "not found" return value
-  if (res == NS_TABLELAYOUT_CELL_NOT_FOUND) res = NS_EDITOR_ELEMENT_NOT_FOUND;
-  return res;
+  nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
+  NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
+
+  nsTableCellFrame* cellFrame =
+    tableFrame->GetCellFrameAt(aRowIndex, aColIndex);
+  if (!cellFrame)
+    return NS_ERROR_FAILURE;
+
+  *aIsSelected = cellFrame->IsSelected();
+  cellFrame->GetRowIndex(*aStartRowIndex);
+  cellFrame->GetColIndex(*aStartColIndex);
+  *aRowSpan = cellFrame->GetRowSpan();
+  *aColSpan = cellFrame->GetColSpan();
+  *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
+  *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
+  nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent());
+  domCell.forget(aCell);
+
+  return NS_OK;
 }
 
 // When all you want is the cell
 NS_IMETHODIMP 
 nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell)
 {
-  int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
-  bool    isSelected;
-  return GetCellDataAt(aTable, aRowIndex, aColIndex, aCell, 
-                       &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
-                       &actualRowSpan, &actualColSpan, &isSelected);
+  NS_ENSURE_ARG_POINTER(aCell);
+  *aCell = nullptr;
+
+  nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
+  if (!tableFrame)
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIDOMElement> domCell =
+    do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex));
+  domCell.forget(aCell);
+
+  return NS_OK;
 }
 
 // When all you want are the rowspan and colspan (not exposed in nsITableEditor)
 NS_IMETHODIMP
 nsHTMLEditor::GetCellSpansAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, 
                              int32_t& aActualRowSpan, int32_t& aActualColSpan)
 {
-  nsCOMPtr<nsIDOMElement> cell;    
-  int32_t startRowIndex, startColIndex, rowSpan, colSpan;
-  bool    isSelected;
-  return GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), 
-                       &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
-                       &aActualRowSpan, &aActualColSpan, &isSelected);
+  nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
+  if (!tableFrame)
+    return NS_ERROR_FAILURE;
+
+  aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
+  aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::GetCellContext(nsISelection **aSelection,
                              nsIDOMElement   **aTable,
                              nsIDOMElement   **aCell,
                              nsIDOMNode      **aCellParent, int32_t *aCellOffset,
                              int32_t *aRowIndex, int32_t *aColIndex)
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -202,28 +202,19 @@ gfxContext::Restore()
 
     if (CurrentState().clipWasReset &&
         CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
       PushClipsToDT(mDT);
     }
 
     mStateStack.RemoveElementAt(mStateStack.Length() - 1);
 
-    if ((mPathBuilder || mPath || mPathIsRect) && !mTransformChanged) {
-      // Support here isn't fully correct if the path is continued -after-
-      // the restore. We don't currently have users that do this and we should
-      // make sure there will not be any. Sadly we can't assert this easily.
-      mTransformChanged = true;
-      mPathTransform = mTransform;
-    }
-
     mDT = CurrentState().drawTarget;
 
-    mTransform = CurrentState().transform;
-    mDT->SetTransform(GetDTTransform());
+    ChangeTransform(CurrentState().transform, false);
   }
 }
 
 // drawing
 void
 gfxContext::NewPath()
 {
   if (mCairo) {
@@ -285,16 +276,18 @@ gfxContext::CurrentPoint()
 void
 gfxContext::Stroke()
 {
   if (mCairo) {
     cairo_stroke_preserve(mCairo);
   } else {
     AzureState &state = CurrentState();
     if (mPathIsRect) {
+      MOZ_ASSERT(!mTransformChanged);
+
       mDT->StrokeRect(mRect, GeneralPattern(this),
                       state.strokeOptions,
                       DrawOptions(1.0f, GetOp(), state.aaMode));
     } else {
       EnsurePath();
 
       mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
                   DrawOptions(1.0f, GetOp(), state.aaMode));
@@ -469,20 +462,20 @@ gfxContext::Rectangle(const gfxRect& rec
         rec = ToRect(mat.TransformBounds(newRect));
       }
     }
 
     if (!mPathBuilder && !mPathIsRect) {
       mPathIsRect = true;
       mRect = rec;
       return;
-    } else if (!mPathBuilder) {
-      EnsurePathBuilder();
     }
-    
+
+    EnsurePathBuilder();
+
     mPathBuilder->MoveTo(rec.TopLeft());
     mPathBuilder->LineTo(rec.TopRight());
     mPathBuilder->LineTo(rec.BottomRight());
     mPathBuilder->LineTo(rec.BottomLeft());
     mPathBuilder->Close();
   }
 }
 
@@ -1133,17 +1126,19 @@ gfxContext::Clip(const gfxRect& rect)
 }
 
 void
 gfxContext::Clip()
 {
   if (mCairo) {
     cairo_clip_preserve(mCairo);
   } else {
-    if (mPathIsRect && !mTransformChanged) {
+    if (mPathIsRect) {
+      MOZ_ASSERT(!mTransformChanged);
+
       AzureState::PushedClip clip = { NULL, mRect, mTransform };
       CurrentState().pushedClips.AppendElement(clip);
       mDT->PushClipRect(mRect);
     } else {
       EnsurePath();
       mDT->PushClip(mPath);
       AzureState::PushedClip clip = { mPath, Rect(), mTransform };
       CurrentState().pushedClips.AppendElement(clip);
@@ -1945,56 +1940,75 @@ gfxContext::EnsurePath()
   EnsurePathBuilder();
   mPath = mPathBuilder->Finish();
   mPathBuilder = NULL;
 }
 
 void
 gfxContext::EnsurePathBuilder()
 {
-  if (mPathBuilder) {
+  if (mPathBuilder && !mTransformChanged) {
     return;
   }
 
   if (mPath) {
-    mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
-    mPath = NULL;
+    if (!mTransformChanged) {
+      mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
+      mPath = NULL;
+    } else {
+      Matrix invTransform = mTransform;
+      invTransform.Invert();
+      Matrix toNewUS = mPathTransform * invTransform;
+      mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
+    }
+    return;
   }
 
-  mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
+  DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
+
+  if (!mPathBuilder) {
+    mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
+
+    if (mPathIsRect) {
+      mPathBuilder->MoveTo(mRect.TopLeft());
+      mPathBuilder->LineTo(mRect.TopRight());
+      mPathBuilder->LineTo(mRect.BottomRight());
+      mPathBuilder->LineTo(mRect.BottomLeft());
+      mPathBuilder->Close();
+    }
+  }
 
-  if (mPathIsRect && !mTransformChanged) {
-    mPathBuilder->MoveTo(mRect.TopLeft());
-    mPathBuilder->LineTo(mRect.TopRight());
-    mPathBuilder->LineTo(mRect.BottomRight());
-    mPathBuilder->LineTo(mRect.BottomLeft());
-    mPathBuilder->Close();
-  } else if (mPathIsRect) {
-    mTransformChanged = false;
-    Matrix mat = mTransform;
-    mat.Invert();
-    mat = mPathTransform * mat;
-    mPathBuilder->MoveTo(mat * mRect.TopLeft());
-    mPathBuilder->LineTo(mat * mRect.TopRight());
-    mPathBuilder->LineTo(mat * mRect.BottomRight());
-    mPathBuilder->LineTo(mat * mRect.BottomLeft());
-    mPathBuilder->Close();
+  if (mTransformChanged) {
+    // This could be an else if since this should never happen when
+    // mPathBuilder is NULL and mPath is NULL. But this way we can assert
+    // if all the state is as expected.
+    MOZ_ASSERT(oldPath);
+    MOZ_ASSERT(!mPathIsRect);
+
+    Matrix invTransform = mTransform;
+    invTransform.Invert();
+    Matrix toNewUS = mPathTransform * invTransform;
+
+    RefPtr<Path> path = mPathBuilder->Finish();
+    mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
   }
 
   mPathIsRect = false;
 }
 
 void
 gfxContext::FillAzure(Float aOpacity)
 {
   AzureState &state = CurrentState();
 
   CompositionOp op = GetOp();
 
-  if (mPathIsRect && !mTransformChanged) {
+  if (mPathIsRect) {
+    MOZ_ASSERT(!mTransformChanged);
+
     if (state.opIsClear) {
       mDT->ClearRect(mRect);
     } else if (op == OP_SOURCE) {
       // Emulate cairo operator source which is bound by mask!
       mDT->ClearRect(mRect);
       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
     } else {
       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
@@ -2064,55 +2078,62 @@ gfxContext::GetOp()
       return OP_SOURCE;
     }
   }
 }
 
 /* SVG font code can change the transform after having set the pattern on the
  * context. When the pattern is set it is in user space, if the transform is
  * changed after doing so the pattern needs to be converted back into userspace.
- * We just store the old pattern here so that we only do the work needed here
- * if the pattern is actually used.
+ * We just store the old pattern transform here so that we only do the work
+ * needed here if the pattern is actually used.
+ * We need to avoid doing this when this ChangeTransform comes from a restore,
+ * since the current pattern and the current transform are both part of the
+ * state we know the new CurrentState()'s values are valid. But if we assume
+ * a change they might become invalid since patternTransformChanged is part of
+ * the state and might be false for the restored AzureState.
  */
 void
-gfxContext::ChangeTransform(const Matrix &aNewMatrix)
+gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
 {
   AzureState &state = CurrentState();
 
-  if ((state.pattern || state.sourceSurface)
+  if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
       && !state.patternTransformChanged) {
     state.patternTransform = mTransform;
     state.patternTransformChanged = true;
   }
 
-  if (mPathBuilder || mPathIsRect) {
+  if (mPathIsRect) {
     Matrix invMatrix = aNewMatrix;
     
     invMatrix.Invert();
 
     Matrix toNewUS = mTransform * invMatrix;
 
-    if (toNewUS.IsRectilinear() && mPathIsRect) {
+    if (toNewUS.IsRectilinear()) {
       mRect = toNewUS.TransformBounds(mRect);
       mRect.NudgeToIntegers();
-    } else if (mPathIsRect) {
+    } else {
       mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
       
       mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
       mPathBuilder->LineTo(toNewUS * mRect.TopRight());
       mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
       mPathBuilder->LineTo(toNewUS * mRect.BottomLeft());
       mPathBuilder->Close();
-    } else {
-      RefPtr<Path> path = mPathBuilder->Finish();
-      // Create path in device space.
-      mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
+
+      mPathIsRect = false;
     }
+
     // No need to consider the transform changed now!
     mTransformChanged = false;
+  } else if ((mPath || mPathBuilder) && !mTransformChanged) {
+    mTransformChanged = true;
+    mPathTransform = mTransform;
   }
 
   mTransform = aNewMatrix;
 
   mDT->SetTransform(GetDTTransform());
 }
 
 Rect
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -760,17 +760,17 @@ private:
 
   // This ensures mPath contains a valid path (in user space!)
   void EnsurePath();
   // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
   void EnsurePathBuilder();
   void FillAzure(mozilla::gfx::Float aOpacity);
   void PushClipsToDT(mozilla::gfx::DrawTarget *aDT);
   CompositionOp GetOp();
-  void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix);
+  void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix, bool aUpdatePatternTransform = true);
   Rect GetAzureDeviceSpaceClipBounds();
   Matrix GetDeviceTransform() const;
   Matrix GetDTTransform() const;
   void PushNewDT(gfxASurface::gfxContentType content);
 
   bool mPathIsRect;
   bool mTransformChanged;
   Matrix mPathTransform;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -305,16 +305,17 @@ CPPSRCS +=	MIR.cpp \
 		Safepoints.cpp \
 		StupidAllocator.cpp \
 		TypeOracle.cpp \
 		TypePolicy.cpp \
 		ValueNumbering.cpp \
 		RangeAnalysis.cpp \
 		VMFunctions.cpp \
 		AliasAnalysis.cpp \
+		UnreachableCodeElimination.cpp \
 		$(NULL)
 endif #ENABLE_ION
 ifeq (86, $(findstring 86,$(TARGET_CPU)))
 ifdef ENABLE_ION
 CPPSRCS +=	CodeGenerator-x86-shared.cpp
 CPPSRCS +=	IonFrames-x86-shared.cpp
 CPPSRCS +=	MoveEmitter-x86-shared.cpp
 CPPSRCS +=	Assembler-x86-shared.cpp
--- a/js/src/config/makefiles/xpcshell.mk
+++ b/js/src/config/makefiles/xpcshell.mk
@@ -10,17 +10,17 @@ ifndef INCLUDED_TESTS_XPCSHELL_MK #{
 
 ifdef XPCSHELL_TESTS #{
 
 ifndef relativesrcdir
 $(error Must define relativesrcdir when defining XPCSHELL_TESTS.)
 endif
 
 define _INSTALL_TESTS
-$(call install_cmd, $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
+$(call install_cmd, $(filter-out %~,$(wildcard $(srcdir)/$(dir)/*)) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 endef # do not remove the blank line!
 
 SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
 
 testxpcsrcdir = $(topsrcdir)/testing/xpcshell
 
 libs:: libs-xpcshell-tests
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2070,17 +2070,17 @@ BindLet(JSContext *cx, BindData *data, H
     }
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
     RootedId id(cx, NameToId(name));
-    Shape *shape = StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared);
+    RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared));
     if (!shape) {
         if (redeclared)
             ReportRedeclaration(cx, parser, pn, false, name);
         return false;
     }
 
     /* Store pn in the static block object. */
     blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
--- a/js/src/frontend/SharedContext-inl.h
+++ b/js/src/frontend/SharedContext-inl.h
@@ -118,17 +118,17 @@ frontend::LexicalLookup(ContextT *ct, Ha
         if (stmt->type == STMT_WITH)
             break;
 
         // Skip statements that do not introduce a new scope
         if (!stmt->isBlockScope)
             continue;
 
         StaticBlockObject &blockObj = *stmt->blockObj;
-        Shape *shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom));
+        UnrootedShape shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom));
         if (shape) {
             JS_ASSERT(shape->hasShortID());
 
             if (slotp)
                 *slotp = blockObj.stackDepth() + shape->shortid();
             return stmt;
         }
     }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -66,30 +66,30 @@ PushMarkStack(GCMarker *gcmarker, JSObje
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, Shape *thing);
+PushMarkStack(GCMarker *gcmarker, UnrootedShape thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
 static void MarkChildren(JSTracer *trc, JSScript *script);
-static void MarkChildren(JSTracer *trc, Shape *shape);
+static void MarkChildren(JSTracer *trc, UnrootedShape shape);
 static void MarkChildren(JSTracer *trc, UnrootedBaseShape base);
 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
 static void MarkChildren(JSTracer *trc, ion::IonCode *code);
 #if JS_HAS_XML_SUPPORT
 static void MarkChildren(JSTracer *trc, JSXML *xml);
 #endif
 
 } /* namespace gc */
@@ -287,22 +287,24 @@ Is##base##Marked(type **thingp)         
 }                                                                                                 \
                                                                                                   \
 bool                                                                                              \
 Is##base##Marked(EncapsulatedPtr<type> *thingp)                                                   \
 {                                                                                                 \
     return IsMarked<type>(thingp->unsafeGet());                                                   \
 }                                                                                                 \
                                                                                                   \
-bool Is##base##AboutToBeFinalized(type **thingp)                                                  \
+bool                                                                                              \
+Is##base##AboutToBeFinalized(type **thingp)                                                       \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp);                                                    \
 }                                                                                                 \
                                                                                                   \
-bool Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp)                                  \
+bool                                                                                              \
+Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp)                                       \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp->unsafeGet());                                       \
 }
 
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(IonCode, ion::IonCode)
 DeclMarkerImpl(Object, ArgumentsObject)
@@ -726,20 +728,20 @@ PushMarkStack(GCMarker *gcmarker, JSScri
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, Shape *shape);
+ScanShape(GCMarker *gcmarker, UnrootedShape shape);
 
 static void
-PushMarkStack(GCMarker *gcmarker, Shape *thing)
+PushMarkStack(GCMarker *gcmarker, UnrootedShape thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
@@ -761,17 +763,17 @@ PushMarkStack(GCMarker *gcmarker, Unroot
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, Shape *shape)
+ScanShape(GCMarker *gcmarker, UnrootedShape shape)
 {
   restart:
     PushMarkStack(gcmarker, shape->base());
 
     const EncapsulatedId &id = shape->propidRef();
     if (JSID_IS_STRING(id))
         PushMarkStack(gcmarker, JSID_TO_STRING(id));
     else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
@@ -925,17 +927,17 @@ gc::MarkChildren(JSTracer *trc, JSString
 
 static void
 gc::MarkChildren(JSTracer *trc, JSScript *script)
 {
     script->markChildren(trc);
 }
 
 static void
-gc::MarkChildren(JSTracer *trc, Shape *shape)
+gc::MarkChildren(JSTracer *trc, UnrootedShape shape)
 {
     shape->markChildren(trc);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, UnrootedBaseShape base)
 {
     base->markChildren(trc);
@@ -985,17 +987,17 @@ MarkCycleCollectorChildren(JSTracer *trc
  * This function is used by the cycle collector to trace through a
  * shape. The cycle collector does not care about shapes or base
  * shapes, so those are not marked. Instead, any shapes or base shapes
  * that are encountered have their children marked. Stack space is
  * bounded. If two shapes in a row have the same parent pointer, the
  * parent pointer will only be marked once.
  */
 void
-gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
+gc::MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape)
 {
     JSObject *prevParent = NULL;
     do {
         MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
         MarkId(trc, &shape->propidRef(), "propid");
         shape = shape->previous();
     } while (shape);
 }
@@ -1016,17 +1018,17 @@ ScanTypeObject(GCMarker *gcmarker, types
     if (TaggedProto(type->proto).isObject())
         PushMarkStack(gcmarker, type->proto);
 
     if (type->singleton && !type->lazy())
         PushMarkStack(gcmarker, type->singleton);
 
     if (type->newScript) {
         PushMarkStack(gcmarker, type->newScript->fun);
-        PushMarkStack(gcmarker, type->newScript->shape);
+        PushMarkStack(gcmarker, type->newScript->shape.get());
     }
 
     if (type->interpretedFunction)
         PushMarkStack(gcmarker, type->interpretedFunction);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
@@ -1351,17 +1353,17 @@ GCMarker::processMarkStackTop(SliceBudge
         if (budget.isOverBudget()) {
             pushObject(obj);
             return;
         }
 
         types::TypeObject *type = obj->typeFromGC();
         PushMarkStack(this, type);
 
-        Shape *shape = obj->lastProperty();
+        UnrootedShape shape = obj->lastProperty();
         PushMarkStack(this, shape);
 
         /* Call the trace hook if necessary. */
         Class *clasp = shape->getObjectClass();
         if (clasp->trace) {
             if (clasp == &ArrayClass) {
                 JS_ASSERT(!shape->isNative());
                 vp = obj->getDenseArrayElements();
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -231,17 +231,17 @@ void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 /*
  * Trace through the shape and any shapes it contains to mark
  * non-shape children. This is exposed to the JS API as
  * JS_TraceShapeCycleCollectorChildren.
  */
 void
-MarkCycleCollectorChildren(JSTracer *trc, Shape *shape);
+MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape);
 
 void
 PushArena(GCMarker *gcmarker, ArenaHeader *aheader);
 
 /*** Generic ***/
 
 /*
  * The Mark() functions interface should only be used by code that must be
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -274,25 +274,27 @@ class MutableHandle : public js::Mutable
     template <typename S>
     MutableHandle(MutableHandle<S> handle,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
         this->ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     template <typename S>
-    inline
-    MutableHandle(js::Rooted<S> *root,
-                  typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
+    inline MutableHandle(js::Rooted<S> *root,
+                         typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     void set(T v) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(v));
         *ptr = v;
     }
 
+    template <typename S>
+    inline void set(const js::Unrooted<S> &v);
+
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
      *
      * Create a MutableHandle from a raw location of a T.
      */
     static MutableHandle fromMarkedLocation(T *p) {
@@ -473,16 +475,23 @@ class Unrooted
     /* See notes for Unrooted::Unrooted(const T &) */
     Unrooted &operator=(T other) {
         JS_ASSERT(other != UninitializedTag());
         if (ptr_ == UninitializedTag())
             EnterAssertNoGCScope();
         ptr_ = other;
         return *this;
     }
+    Unrooted &operator=(Unrooted other) {
+        JS_ASSERT(other.ptr_ != UninitializedTag());
+        if (ptr_ == UninitializedTag())
+            EnterAssertNoGCScope();
+        ptr_ = other.ptr_;
+        return *this;
+    }
 
     operator T() const { return (ptr_ == UninitializedTag()) ? NULL : ptr_; }
     T *operator&() { return &ptr_; }
     const T operator->() const { JS_ASSERT(ptr_ != UninitializedTag()); return ptr_; }
     bool operator==(const T &other) { return ptr_ == other; }
     bool operator!=(const T &other) { return ptr_ != other; }
 
   private:
@@ -854,16 +863,23 @@ Handle<T>::Handle(MutableHandle<S> &root
 template <typename T> template <typename S>
 inline
 MutableHandle<T>::MutableHandle(js::Rooted<S> *root,
                                 typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = root->address();
 }
 
+template <typename T> template <typename S>
+inline void MutableHandle<T>::set(const js::Unrooted<S> &v)
+{
+    JS_ASSERT(!js::RootMethods<T>::poisoned(v));
+    *ptr = static_cast<S>(v);
+}
+
 /*
  * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is
  * attempted while the guard object is live.  If you have a GC-unsafe operation
  * to perform, use this guard object to protect your operation.
  */
 class AutoAssertNoGC
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -526,16 +526,27 @@ AutoGCRooter::trace(JSTracer *trc)
 
       case SCRIPTVECTOR: {
         AutoScriptVector::VectorImpl &vector = static_cast<AutoScriptVector *>(this)->vector;
         for (size_t i = 0; i < vector.length(); i++)
             MarkScriptRoot(trc, &vector[i], "AutoScriptVector element");
         return;
       }
 
+      case OBJOBJHASHMAP: {
+        AutoObjectObjectHashMap::HashMapImpl &map = static_cast<AutoObjectObjectHashMap *>(this)->map;
+        for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) {
+            RawObject key = e.front().key;
+            MarkObjectRoot(trc, (RawObject *) &e.front().key, "AutoObjectObjectHashMap key");
+            JS_ASSERT(key == e.front().key);
+            MarkObjectRoot(trc, &e.front().value, "AutoObjectObjectHashMap value");
+        }
+        return;
+      }
+
       case PROPDESC: {
         PropDesc::AutoRooter *rooter = static_cast<PropDesc::AutoRooter *>(this);
         MarkValueRoot(trc, &rooter->pd->pd_, "PropDesc::AutoRooter pd");
         MarkValueRoot(trc, &rooter->pd->value_, "PropDesc::AutoRooter value");
         MarkValueRoot(trc, &rooter->pd->get_, "PropDesc::AutoRooter get");
         MarkValueRoot(trc, &rooter->pd->set_, "PropDesc::AutoRooter set");
         return;
       }
--- a/js/src/gdb/mozilla/JSObject.py
+++ b/js/src/gdb/mozilla/JSObject.py
@@ -1,30 +1,30 @@
 # Pretty-printers for SpiderMonkey JSObjects.
 
 import gdb
 import mozilla.JSString
-import mozilla.prettyprinters
+import mozilla.prettyprinters as prettyprinters
 from mozilla.prettyprinters import ptr_pretty_printer
 from mozilla.Root import deref
 
-mozilla.prettyprinters.clear_module_printers(__name__)
+prettyprinters.clear_module_printers(__name__)
 
 class JSObjectTypeCache(object):
     def __init__(self, value, cache):
         baseshape_flags = gdb.lookup_type('js::BaseShape::Flag')
-        self.flag_DELEGATE = baseshape_flags['js::BaseShape::DELEGATE'].bitpos
+        self.flag_DELEGATE = prettyprinters.enum_value(baseshape_flags, 'js::BaseShape::DELEGATE')
         self.func_ptr_type = gdb.lookup_type('JSFunction').pointer()
 
 # There should be no need to register this for JSFunction as well, since we
 # search for pretty-printers under the names of base classes, and
 # JSFunction has JSObject as a base class.
 
 @ptr_pretty_printer('JSObject')
-class JSObjectPtr(mozilla.prettyprinters.Pointer):
+class JSObjectPtr(prettyprinters.Pointer):
     def __init__(self, value, cache):
         super(JSObjectPtr, self).__init__(value, cache)
         if not cache.mod_JSObject:
             cache.mod_JSObject = JSObjectTypeCache(value, cache)
         self.otc = cache.mod_JSObject
 
     def summary(self):
         shape = deref(self.value['shape_'])
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -306,8 +306,27 @@ class Pointer(object):
             summary = self.summary()
         except gdb.MemoryError as r:
             summary = str(r)
         v = '(%s) %s %s' % (self.value.type, address, summary)
         return v
 
     def summary(self):
         raise NotImplementedError
+
+field_enum_value = None
+
+# Given |t|, a gdb.Type instance representing an enum type, return the
+# numeric value of the enum value named |name|.
+#
+# Pre-2012-4-18 versions of GDB store the value of an enum member on the
+# gdb.Field's 'bitpos' attribute; later versions store it on the 'enumval'
+# attribute. This function retrieves the value from either.
+def enum_value(t, name):
+    global field_enum_value
+    f = t[name]
+    # Monkey-patching is a-okay in polyfills! Just because.
+    if not field_enum_value:
+        if hasattr(f, 'enumval'):
+            field_enum_value = lambda f: f.enumval
+        else:
+            field_enum_value = lambda f: f.bitpos
+    return field_enum_value(f)
--- a/js/src/gdb/run-tests.py
+++ b/js/src/gdb/run-tests.py
@@ -334,15 +334,15 @@ def main(argv):
         sys.exit(1)
 
     # Run the tests.
     try:
         summary.start()
         run_tests(test_list, summary)
         summary.finish()
     except OSError as err:
-        sys.stderr.write("Error running tests: %s\n", (err,))
+        sys.stderr.write("Error running tests: %s\n" % (err,))
         sys.exit(1)
 
     sys.exit(0)
 
 if __name__ == '__main__':
     main(sys.argv[1:])
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -2348,41 +2348,41 @@ CodeGenerator::visitConcat(LConcat *lir)
 {
     pushArg(ToRegister(lir->rhs()));
     pushArg(ToRegister(lir->lhs()));
     if (!callVM(ConcatStringsInfo, lir))
         return false;
     return true;
 }
 
-typedef bool (*EnsureLinearFn)(JSContext *, JSString *);
-static const VMFunction EnsureLinearInfo = FunctionInfo<EnsureLinearFn>(JSString::ensureLinear);
+typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *);
+static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(ion::CharCodeAt);
 
 bool
 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
 {
     Register str = ToRegister(lir->str());
     Register index = ToRegister(lir->index());
     Register output = ToRegister(lir->output());
 
-    OutOfLineCode *ool = oolCallVM(EnsureLinearInfo, lir, (ArgList(), str), StoreNothing());
+    OutOfLineCode *ool = oolCallVM(CharCodeAtInfo, lir, (ArgList(), str, index), StoreRegisterTo(output));
     if (!ool)
         return false;
 
     Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
     masm.loadPtr(lengthAndFlagsAddr, output);
 
     masm.branchTest32(Assembler::Zero, output, Imm32(JSString::FLAGS_MASK), ool->entry());
-    masm.bind(ool->rejoin());
 
     // getChars
     Address charsAddr(str, JSString::offsetOfChars());
     masm.loadPtr(charsAddr, output);
     masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
 
+    masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSFlatString *(*StringFromCharCodeFn)(JSContext *, int32_t);
 static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(ion::StringFromCharCode);
 
 bool
 CodeGenerator::visitFromCharCode(LFromCharCode *lir)
--- a/js/src/ion/CompilerRoot.h
+++ b/js/src/ion/CompilerRoot.h
@@ -36,26 +36,29 @@ class CompilerRoot : public CompilerRoot
         JS_ASSERT(!ptr);
         ptr = root;
         next = rootList;
         rootList = this;
     }
 
   public:
     operator T () const { return static_cast<T>(ptr); }
+    operator Unrooted<T> () const { return static_cast<T>(ptr); }
     T operator ->() const { return static_cast<T>(ptr); }
 
   private:
     CompilerRoot() MOZ_DELETE;
     CompilerRoot(const CompilerRoot<T> &) MOZ_DELETE;
     CompilerRoot<T> &operator =(const CompilerRoot<T> &) MOZ_DELETE;
 };
 
-typedef CompilerRoot<JSObject*>   CompilerRootObject;
+typedef CompilerRoot<JSObject*> CompilerRootObject;
 typedef CompilerRoot<JSFunction*> CompilerRootFunction;
+typedef CompilerRoot<JSScript*> CompilerRootScript;
 typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
+typedef CompilerRoot<Shape*> CompilerRootShape;
 typedef CompilerRoot<Value> CompilerRootValue;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_gc_h__
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -17,16 +17,17 @@
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "LinearScan.h"
 #include "jscompartment.h"
 #include "jsworkers.h"
 #include "IonCompartment.h"
 #include "CodeGenerator.h"
 #include "StupidAllocator.h"
+#include "UnreachableCodeElimination.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/Lowering-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/Lowering-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/Lowering-arm.h"
 #endif
@@ -809,48 +810,49 @@ CompileBackEnd(MIRGenerator *mir)
     if (!BuildDominatorTree(graph))
         return NULL;
     // No spew: graph not changed.
 
     if (mir->shouldCancel("Dominator Tree"))
         return NULL;
 
     // This must occur before any code elimination.
-    if (!EliminatePhis(mir, graph))
+    if (!EliminatePhis(mir, graph, AggressiveObservability))
         return NULL;
     IonSpewPass("Eliminate phis");
     AssertGraphCoherency(graph);
 
     if (mir->shouldCancel("Eliminate phis"))
         return NULL;
 
     if (!BuildPhiReverseMapping(graph))
         return NULL;
+    AssertExtendedGraphCoherency(graph);
     // No spew: graph not changed.
 
     if (mir->shouldCancel("Phi reverse mapping"))
         return NULL;
 
     // This pass also removes copies.
     if (!ApplyTypeInformation(mir, graph))
         return NULL;
     IonSpewPass("Apply types");
-    AssertGraphCoherency(graph);
+    AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("Apply types"))
         return NULL;
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
     if (js_IonOptions.licm || js_IonOptions.gvn) {
         AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
             return NULL;
         IonSpewPass("Alias analysis");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Alias analysis"))
             return NULL;
 
         // Eliminating dead resume point operands requires basic block
         // instructions to be numbered. Reuse the numbering computed during
         // alias analysis.
         if (!EliminateDeadResumePointOperands(mir, graph))
@@ -860,75 +862,86 @@ CompileBackEnd(MIRGenerator *mir)
             return NULL;
     }
 
     if (js_IonOptions.edgeCaseAnalysis) {
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeEarly())
             return NULL;
         IonSpewPass("Edge Case Analysis (Early)");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Edge Case Analysis (Early)"))
             return NULL;
     }
 
     if (js_IonOptions.gvn) {
         ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic);
         if (!gvn.analyze())
             return NULL;
         IonSpewPass("GVN");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("GVN"))
             return NULL;
     }
 
+    if (js_IonOptions.uce) {
+        UnreachableCodeElimination uce(mir, graph);
+        if (!uce.analyze())
+            return NULL;
+        IonSpewPass("UCE");
+        AssertExtendedGraphCoherency(graph);
+    }
+
+    if (mir->shouldCancel("UCE"))
+        return NULL;
+
     if (js_IonOptions.licm) {
         LICM licm(mir, graph);
         if (!licm.analyze())
             return NULL;
         IonSpewPass("LICM");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("LICM"))
             return NULL;
     }
 
     if (js_IonOptions.rangeAnalysis) {
         RangeAnalysis r(graph);
         if (!r.addBetaNobes())
             return NULL;
         IonSpewPass("Beta");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
             return NULL;
 
         if (!r.analyze())
             return NULL;
         IonSpewPass("Range Analysis");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Range Analysis"))
             return NULL;
 
         if (!r.removeBetaNobes())
             return NULL;
         IonSpewPass("De-Beta");
-        AssertGraphCoherency(graph);
+        AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA De-Beta"))
             return NULL;
     }
 
     if (!EliminateDeadCode(mir, graph))
         return NULL;
     IonSpewPass("DCE");
-    AssertGraphCoherency(graph);
+    AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("DCE"))
         return NULL;
 
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
     if (js_IonOptions.edgeCaseAnalysis) {
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -65,19 +65,24 @@ struct IonOptions
 
     // Toggles whether Edge Case Analysis is used.
     //
     // Default: true
     bool edgeCaseAnalysis;
 
     // Toggles whether Range Analysis is used.
     //
-    // Default: false
+    // Default: true
     bool rangeAnalysis;
 
+    // Toggles whether Unreachable Code Elimination is performed.
+    //
+    // Default: true
+    bool uce;
+
     // Toggles whether compilation occurs off the main thread.
     //
     // Default: true iff there are at least two CPUs available
     bool parallelCompilation;
 
     // How many invocations or loop iterations are needed before functions
     // are compiled.
     //
@@ -173,16 +178,17 @@ struct IonOptions
         gvnIsOptimistic(true),
         licm(true),
         osr(true),
         limitScriptSize(true),
         registerAllocator(RegisterAllocator_LSRA),
         inlining(true),
         edgeCaseAnalysis(true),
         rangeAnalysis(true),
+        uce(true),
         parallelCompilation(false),
         usesBeforeCompile(10240),
         usesBeforeCompileNoJaeger(40),
         usesBeforeInlining(usesBeforeCompile),
         maxStackArgs(4096),
         maxInlineDepth(3),
         smallFunctionMaxBytecodeLength(100),
         smallFunctionUsesBeforeInlining(usesBeforeInlining / 4),
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -82,17 +82,21 @@ ion::EliminateDeadResumePointOperands(MI
 
             // Check if this instruction's result is only used within the
             // current block, and keep track of its last use in a definition
             // (not resume point). This requires the instructions in the block
             // to be numbered, ensured by running this immediately after alias
             // analysis.
             uint32_t maxDefinition = 0;
             for (MUseDefIterator uses(*ins); uses; uses++) {
-                if (uses.def()->block() != *block || uses.def()->isBox() || uses.def()->isPassArg()) {
+                if (uses.def()->block() != *block ||
+                    uses.def()->isBox() ||
+                    uses.def()->isPassArg() ||
+                    uses.def()->isPhi())
+                {
                     maxDefinition = UINT32_MAX;
                     break;
                 }
                 maxDefinition = Max(maxDefinition, uses.def()->id());
             }
             if (maxDefinition == UINT32_MAX)
                 continue;
 
@@ -154,29 +158,46 @@ ion::EliminateDeadCode(MIRGenerator *mir
             }
         }
     }
 
     return true;
 }
 
 static inline bool
-IsPhiObservable(MPhi *phi)
+IsPhiObservable(MPhi *phi, Observability observe)
 {
     // If the phi has uses which are not reflected in SSA, then behavior in the
     // interpreter may be affected by removing the phi.
     if (phi->isFolded())
         return true;
 
-    // Check for any SSA uses. Note that this skips reading resume points,
-    // which we don't count as actual uses. If the only uses are resume points,
-    // then the SSA name is never consumed by the program.
-    for (MUseDefIterator iter(phi); iter; iter++) {
-        if (!iter.def()->isPhi())
-            return true;
+    // Check for uses of this phi node outside of other phi nodes.
+    // Note that, initially, we skip reading resume points, which we
+    // don't count as actual uses. If the only uses are resume points,
+    // then the SSA name is never consumed by the program.  However,
+    // after optimizations have been performed, it's possible that the
+    // actual uses in the program have been (incorrectly) optimized
+    // away, so we must be more conservative and consider resume
+    // points as well.
+    switch (observe) {
+      case AggressiveObservability:
+        for (MUseDefIterator iter(phi); iter; iter++) {
+            if (!iter.def()->isPhi())
+                return true;
+        }
+        break;
+
+      case ConservativeObservability:
+        for (MUseIterator iter(phi->usesBegin()); iter != phi->usesEnd(); iter++) {
+            if (!iter->node()->isDefinition() ||
+                !iter->node()->toDefinition()->isPhi())
+                return true;
+        }
+        break;
     }
 
     // If the Phi is of the |this| value, it must always be observable.
     uint32_t slot = phi->slot();
     if (slot == 1)
         return true;
 
     // If the Phi is one of the formal argument, and we are using an argument
@@ -190,36 +211,57 @@ IsPhiObservable(MPhi *phi)
             return true;
     }
     return false;
 }
 
 // Handles cases like:
 //    x is phi(a, x) --> a
 //    x is phi(a, a) --> a
-static inline MDefinition *
+inline MDefinition *
 IsPhiRedundant(MPhi *phi)
 {
-    MDefinition *first = phi->getOperand(0);
-
-    for (size_t i = 1; i < phi->numOperands(); i++) {
-        if (phi->getOperand(i) != first && phi->getOperand(i) != phi)
-            return NULL;
-    }
+    MDefinition *first = phi->operandIfRedundant();
+    if (first == NULL)
+        return NULL;
 
     // Propagate the Folded flag if |phi| is replaced with another phi.
     if (phi->isFolded())
         first->setFoldedUnchecked();
 
     return first;
 }
 
 bool
-ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph)
+ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph,
+                   Observability observe)
 {
+    // Eliminates redundant or unobservable phis from the graph.  A
+    // redundant phi is something like b = phi(a, a) or b = phi(a, b),
+    // both of which can be replaced with a.  An unobservable phi is
+    // one that whose value is never used in the program.
+    //
+    // Note that we must be careful not to eliminate phis representing
+    // values that the interpreter will require later.  When the graph
+    // is first constructed, we can be more aggressive, because there
+    // is a greater correspondence between the CFG and the bytecode.
+    // After optimizations such as GVN have been performed, however,
+    // the bytecode and CFG may not correspond as closely to one
+    // another.  In that case, we must be more conservative.  The flag
+    // |conservativeObservability| is used to indicate that eliminate
+    // phis is being run after some optimizations have been performed,
+    // and thus we should use more conservative rules about
+    // observability.  The particular danger is that we can optimize
+    // away uses of a phi because we think they are not executable,
+    // but the foundation for that assumption is false TI information
+    // that will eventually be invalidated.  Therefore, if
+    // |conservativeObservability| is set, we will consider any use
+    // from a resume point to be observable.  Otherwise, we demand a
+    // use from an actual instruction.
+
     Vector<MPhi *, 16, SystemAllocPolicy> worklist;
 
     // Add all observable phis to a worklist. We use the "in worklist" bit to
     // mean "this phi is live".
     for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
         if (mir->shouldCancel("Eliminate Phis (populate loop)"))
             return false;
 
@@ -232,17 +274,17 @@ ion::EliminatePhis(MIRGenerator *mir, MI
             // If the phi is redundant, remove it here.
             if (MDefinition *redundant = IsPhiRedundant(*iter)) {
                 iter->replaceAllUsesWith(redundant);
                 iter = block->discardPhiAt(iter);
                 continue;
             }
 
             // Enqueue observable Phis.
-            if (IsPhiObservable(*iter)) {
+            if (IsPhiObservable(*iter, observe)) {
                 iter->setInWorklist();
                 if (!worklist.append(*iter))
                     return false;
             }
             iter++;
         }
     }
 
@@ -867,33 +909,90 @@ AssertReversePostOrder(MIRGraph &graph)
 }
 #endif
 
 void
 ion::AssertGraphCoherency(MIRGraph &graph)
 {
 #ifdef DEBUG
     // Assert successor and predecessor list coherency.
+    uint32_t count = 0;
     for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
+        count++;
+
         for (size_t i = 0; i < block->numSuccessors(); i++)
             JS_ASSERT(CheckSuccessorImpliesPredecessor(*block, block->getSuccessor(i)));
 
         for (size_t i = 0; i < block->numPredecessors(); i++)
             JS_ASSERT(CheckPredecessorImpliesSuccessor(*block, block->getPredecessor(i)));
 
         for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
             for (uint32_t i = 0; i < ins->numOperands(); i++)
                 JS_ASSERT(CheckMarkedAsUse(*ins, ins->getOperand(i)));
         }
     }
 
+    JS_ASSERT(graph.numBlocks() == count);
+
     AssertReversePostOrder(graph);
 #endif
 }
 
+void
+ion::AssertExtendedGraphCoherency(MIRGraph &graph)
+{
+    // Checks the basic GraphCoherency but also other conditions that
+    // do not hold immediately (such as the fact that critical edges
+    // are split)
+
+#ifdef DEBUG
+    AssertGraphCoherency(graph);
+
+    uint32_t idx = 0;
+    for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
+        JS_ASSERT(block->id() == idx++);
+
+        // No critical edges:
+        if (block->numSuccessors() > 1)
+            for (size_t i = 0; i < block->numSuccessors(); i++)
+                JS_ASSERT(block->getSuccessor(i)->numPredecessors() == 1);
+
+        if (block->isLoopHeader()) {
+            JS_ASSERT(block->numPredecessors() == 2);
+            MBasicBlock *backedge = block->getPredecessor(1);
+            JS_ASSERT(backedge->id() >= block->id());
+            JS_ASSERT(backedge->numSuccessors() == 1);
+            JS_ASSERT(backedge->getSuccessor(0) == *block);
+        }
+
+        if (!block->phisEmpty()) {
+            for (size_t i = 0; i < block->numPredecessors(); i++) {
+                MBasicBlock *pred = block->getPredecessor(i);
+                JS_ASSERT(pred->successorWithPhis() == *block);
+                JS_ASSERT(pred->positionInPhiSuccessor() == i);
+            }
+        }
+
+        uint32_t successorWithPhis = 0;
+        for (size_t i = 0; i < block->numSuccessors(); i++)
+            if (!block->getSuccessor(i)->phisEmpty())
+                successorWithPhis++;
+
+        JS_ASSERT(successorWithPhis <= 1);
+        JS_ASSERT_IF(successorWithPhis, block->successorWithPhis() != NULL);
+
+        // I'd like to assert this, but it's not necc. true.  Sometimes we set this
+        // flag to non-NULL just because a successor has multiple preds, even if it
+        // does not actually have any phis.
+        //
+        // JS_ASSERT_IF(!successorWithPhis, block->successorWithPhis() == NULL);
+    }
+#endif
+}
+
 
 struct BoundsCheckInfo
 {
     MBoundsCheck *check;
     uint32_t validUntil;
 };
 
 typedef HashMap<uint32_t,
--- a/js/src/ion/IonAnalysis.h
+++ b/js/src/ion/IonAnalysis.h
@@ -17,18 +17,23 @@ namespace js {
 namespace ion {
 
 class MIRGenerator;
 class MIRGraph;
 
 bool
 SplitCriticalEdges(MIRGraph &graph);
 
+enum Observability {
+    ConservativeObservability,
+    AggressiveObservability
+};
+
 bool
-EliminatePhis(MIRGenerator *mir, MIRGraph &graph);
+EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe);
 
 bool
 EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph);
 
 bool
@@ -41,16 +46,19 @@ bool
 BuildDominatorTree(MIRGraph &graph);
 
 bool
 BuildPhiReverseMapping(MIRGraph &graph);
 
 void
 AssertGraphCoherency(MIRGraph &graph);
 
+void
+AssertExtendedGraphCoherency(MIRGraph &graph);
+
 bool
 EliminateRedundantBoundsChecks(MIRGraph &graph);
 
 class MDefinition;
 
 // Simple linear sum of the form 'n' or 'x + n'.
 struct SimpleLinearSum
 {
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4280,18 +4280,18 @@ IonBuilder::jsop_newobject(HandleObject 
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_initelem()
 {
-    RootedScript scriptRoot(cx, script());
-    if (oracle->propertyWriteCanSpecialize(scriptRoot, pc)) {
+    if (oracle->propertyWriteCanSpecialize(script(), pc)) {
+        RootedScript scriptRoot(cx, script());
         if (oracle->elementWriteIsDenseArray(scriptRoot, pc))
             return jsop_initelem_dense();
     }
 
     return abort("NYI: JSOP_INITELEM supports for non dense objects/arrays.");
 }
 
 bool
@@ -4849,16 +4849,18 @@ TestSingletonPropertyTypes(JSContext *cx
 //     value at the top of the stack.
 // (4) If a type barrier is in place, and has a single type, an unbox
 //     instruction replaces the top of the stack.
 // (5) Lastly, a type barrier instruction replaces the top of the stack.
 bool
 IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual,
                             types::StackTypeSet *observed)
 {
+    AutoAssertNoGC nogc;
+
     // If the instruction has no side effects, we'll resume the entire operation.
     // The actual type barrier will occur in the interpreter. If the
     // instruction is effectful, even if it has a singleton type, there
     // must be a resume point capturing the original def, and resuming
     // to that point will explicitly monitor the new type.
 
     if (!actual) {
         JS_ASSERT(!observed);
@@ -4966,17 +4968,17 @@ IonBuilder::jsop_getgname(HandleProperty
 
     RootedObject globalObj(cx, &script()->global());
     JS_ASSERT(globalObj->isNative());
 
     RootedId id(cx, NameToId(name));
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
-    const js::Shape *shape = globalObj->nativeLookup(cx, id);
+    RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return jsop_getname(name);
 
     types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script(), pc, id);
     if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_getname(name);
@@ -5043,17 +5045,17 @@ IonBuilder::jsop_setgname(HandleProperty
     types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script(), pc, id, &canSpecialize);
 
     // This should only happen for a few names like __proto__.
     if (!canSpecialize || globalObj->watched())
         return jsop_setprop(name);
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
-    const js::Shape *shape = globalObj->nativeLookup(cx, id);
+    RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())