Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Tue, 04 Jun 2013 17:16:15 -0700
changeset 134129 30fe681b27bea80b484fc0b8e70928246eed4f2f
parent 134128 5ccd0592e074a249595230c7b964727afec6df01 (current diff)
parent 134009 43c0123f158b7f6006cf92cf1318abf30494ea8c (diff)
child 134130 79088e422daf4d1af974ba219fc01e13ffa245d2
push id29067
push userryanvm@gmail.com
push dateWed, 05 Jun 2013 20:37:20 +0000
treeherdermozilla-inbound@72fbfb2f8e51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
content/media/test/test_mozLoadFrom.html
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/jswin64.asm
js/src/tests/supporting/test402-browser.js
js/src/tests/supporting/test402-shell.js
js/src/tests/test402/HG-INFO
js/src/tests/test402/LICENSE
js/src/tests/test402/browser.js
js/src/tests/test402/ch06/6.2/6.2.2_a.js
js/src/tests/test402/ch06/6.2/6.2.2_b.js
js/src/tests/test402/ch06/6.2/6.2.2_c.js
js/src/tests/test402/ch06/6.2/6.2.3.js
js/src/tests/test402/ch06/6.2/6.2.4.js
js/src/tests/test402/ch06/6.2/browser.js
js/src/tests/test402/ch06/6.2/shell.js
js/src/tests/test402/ch06/6.3/6.3.1_a.js
js/src/tests/test402/ch06/6.3/6.3.1_b.js
js/src/tests/test402/ch06/6.3/browser.js
js/src/tests/test402/ch06/6.3/shell.js
js/src/tests/test402/ch06/6.4/6.4_a.js
js/src/tests/test402/ch06/6.4/6.4_b.js
js/src/tests/test402/ch06/6.4/6.4_c.js
js/src/tests/test402/ch06/6.4/browser.js
js/src/tests/test402/ch06/6.4/shell.js
js/src/tests/test402/ch06/browser.js
js/src/tests/test402/ch06/shell.js
js/src/tests/test402/ch08/8.0/8.0.js
js/src/tests/test402/ch08/8.0/8.0_L15.js
js/src/tests/test402/ch08/8.0/browser.js
js/src/tests/test402/ch08/8.0/shell.js
js/src/tests/test402/ch08/browser.js
js/src/tests/test402/ch08/shell.js
js/src/tests/test402/ch09/9.1/9.1_a.js
js/src/tests/test402/ch09/9.1/9.1_b.js
js/src/tests/test402/ch09/9.1/browser.js
js/src/tests/test402/ch09/9.1/shell.js
js/src/tests/test402/ch09/9.2/9.2.1_1.js
js/src/tests/test402/ch09/9.2/9.2.1_2.js
js/src/tests/test402/ch09/9.2/9.2.1_3.js
js/src/tests/test402/ch09/9.2/9.2.1_4.js
js/src/tests/test402/ch09/9.2/9.2.1_8_c_ii.js
js/src/tests/test402/ch09/9.2/9.2.1_8_c_vi.js
js/src/tests/test402/ch09/9.2/9.2.2.js
js/src/tests/test402/ch09/9.2/9.2.3_5.js
js/src/tests/test402/ch09/9.2/9.2.5_11_g_ii_2.js
js/src/tests/test402/ch09/9.2/9.2.5_6.js
js/src/tests/test402/ch09/9.2/9.2.6_2.js
js/src/tests/test402/ch09/9.2/9.2.6_4.js
js/src/tests/test402/ch09/9.2/9.2.6_4_b.js
js/src/tests/test402/ch09/9.2/9.2.6_4_c.js
js/src/tests/test402/ch09/9.2/9.2.8_1_c.js
js/src/tests/test402/ch09/9.2/9.2.8_4.js
js/src/tests/test402/ch09/9.2/browser.js
js/src/tests/test402/ch09/9.2/shell.js
js/src/tests/test402/ch09/browser.js
js/src/tests/test402/ch09/shell.js
js/src/tests/test402/ch10/10.1/10.1.1_1.js
js/src/tests/test402/ch10/10.1/10.1.1_10.js
js/src/tests/test402/ch10/10.1/10.1.1_11.js
js/src/tests/test402/ch10/10.1/10.1.1_13.js
js/src/tests/test402/ch10/10.1/10.1.1_19_b.js
js/src/tests/test402/ch10/10.1/10.1.1_19_c.js
js/src/tests/test402/ch10/10.1/10.1.1_20.js
js/src/tests/test402/ch10/10.1/10.1.1_23.js
js/src/tests/test402/ch10/10.1/10.1.1_6.js
js/src/tests/test402/ch10/10.1/10.1.1_a.js
js/src/tests/test402/ch10/10.1/10.1.2.1_4.js
js/src/tests/test402/ch10/10.1/10.1.2_a.js
js/src/tests/test402/ch10/10.1/10.1.3.js
js/src/tests/test402/ch10/10.1/10.1_L15.js
js/src/tests/test402/ch10/10.1/browser.js
js/src/tests/test402/ch10/10.1/shell.js
js/src/tests/test402/ch10/10.2/10.2.1.js
js/src/tests/test402/ch10/10.2/10.2.2_L15.js
js/src/tests/test402/ch10/10.2/10.2.2_a.js
js/src/tests/test402/ch10/10.2/10.2.2_b.js
js/src/tests/test402/ch10/10.2/10.2.3_b.js
js/src/tests/test402/ch10/10.2/browser.js
js/src/tests/test402/ch10/10.2/shell.js
js/src/tests/test402/ch10/10.3/10.3.1.js
js/src/tests/test402/ch10/10.3/10.3.2_1_a_L15.js
js/src/tests/test402/ch10/10.3/10.3.2_1_c.js
js/src/tests/test402/ch10/10.3/10.3.2_CS_a.js
js/src/tests/test402/ch10/10.3/10.3.2_CS_b_NN.js
js/src/tests/test402/ch10/10.3/10.3.2_CS_c_NN.js
js/src/tests/test402/ch10/10.3/10.3.2_CS_d_NN.js
js/src/tests/test402/ch10/10.3/10.3.2_L15.js
js/src/tests/test402/ch10/10.3/10.3.3.js
js/src/tests/test402/ch10/10.3/10.3.3_L15.js
js/src/tests/test402/ch10/10.3/10.3_L15.js
js/src/tests/test402/ch10/10.3/10.3_a.js
js/src/tests/test402/ch10/10.3/10.3_b.js
js/src/tests/test402/ch10/10.3/browser.js
js/src/tests/test402/ch10/10.3/shell.js
js/src/tests/test402/ch10/10.4/10.4_a.js
js/src/tests/test402/ch10/10.4/browser.js
js/src/tests/test402/ch10/10.4/shell.js
js/src/tests/test402/ch10/browser.js
js/src/tests/test402/ch10/shell.js
js/src/tests/test402/ch11/11.1/11.1.1_1.js
js/src/tests/test402/ch11/11.1/11.1.1_15.js
js/src/tests/test402/ch11/11.1/11.1.1_17.js
js/src/tests/test402/ch11/11.1/11.1.1_19.js
js/src/tests/test402/ch11/11.1/11.1.1_20_c.js
js/src/tests/test402/ch11/11.1/11.1.1_21.js
js/src/tests/test402/ch11/11.1/11.1.1_32.js
js/src/tests/test402/ch11/11.1/11.1.1_34.js
js/src/tests/test402/ch11/11.1/11.1.1_6.js
js/src/tests/test402/ch11/11.1/11.1.1_7.js
js/src/tests/test402/ch11/11.1/11.1.1_a.js
js/src/tests/test402/ch11/11.1/11.1.2.1_4.js
js/src/tests/test402/ch11/11.1/11.1.2.js
js/src/tests/test402/ch11/11.1/11.1.3.js
js/src/tests/test402/ch11/11.1/11.1_L15.js
js/src/tests/test402/ch11/11.1/browser.js
js/src/tests/test402/ch11/11.1/shell.js
js/src/tests/test402/ch11/11.2/11.2.1.js
js/src/tests/test402/ch11/11.2/11.2.2_L15.js
js/src/tests/test402/ch11/11.2/11.2.2_a.js
js/src/tests/test402/ch11/11.2/11.2.2_b.js
js/src/tests/test402/ch11/11.2/11.2.3_b.js
js/src/tests/test402/ch11/11.2/browser.js
js/src/tests/test402/ch11/11.2/shell.js
js/src/tests/test402/ch11/11.3/11.3.1.js
js/src/tests/test402/ch11/11.3/11.3.2_1_a_L15.js
js/src/tests/test402/ch11/11.3/11.3.2_1_a_ii.js
js/src/tests/test402/ch11/11.3/11.3.2_1_c.js
js/src/tests/test402/ch11/11.3/11.3.2_FN_1.js
js/src/tests/test402/ch11/11.3/11.3.2_FN_2.js
js/src/tests/test402/ch11/11.3/11.3.2_FN_3_b.js
js/src/tests/test402/ch11/11.3/11.3.2_FN_3_e.js
js/src/tests/test402/ch11/11.3/11.3.2_L15.js
js/src/tests/test402/ch11/11.3/11.3.2_TRF.js
js/src/tests/test402/ch11/11.3/11.3.2_TRP.js
js/src/tests/test402/ch11/11.3/11.3.3.js
js/src/tests/test402/ch11/11.3/11.3.3_L15.js
js/src/tests/test402/ch11/11.3/11.3_L15.js
js/src/tests/test402/ch11/11.3/11.3_a.js
js/src/tests/test402/ch11/11.3/11.3_b.js
js/src/tests/test402/ch11/11.3/browser.js
js/src/tests/test402/ch11/11.3/shell.js
js/src/tests/test402/ch11/11.4/11.4_a.js
js/src/tests/test402/ch11/11.4/browser.js
js/src/tests/test402/ch11/11.4/shell.js
js/src/tests/test402/ch11/browser.js
js/src/tests/test402/ch11/shell.js
js/src/tests/test402/ch12/12.1/12.1.1_1.js
js/src/tests/test402/ch12/12.1/12.1.1_18.js
js/src/tests/test402/ch12/12.1/12.1.1_22.js
js/src/tests/test402/ch12/12.1/12.1.1_23.js
js/src/tests/test402/ch12/12.1/12.1.1_25.js
js/src/tests/test402/ch12/12.1/12.1.1_5.js
js/src/tests/test402/ch12/12.1/12.1.1_6.js
js/src/tests/test402/ch12/12.1/12.1.1_TDTO.js
js/src/tests/test402/ch12/12.1/12.1.1_a.js
js/src/tests/test402/ch12/12.1/12.1.2.1_4.js
js/src/tests/test402/ch12/12.1/12.1.2.js
js/src/tests/test402/ch12/12.1/12.1.3.js
js/src/tests/test402/ch12/12.1/12.1_L15.js
js/src/tests/test402/ch12/12.1/browser.js
js/src/tests/test402/ch12/12.1/shell.js
js/src/tests/test402/ch12/12.2/12.2.1.js
js/src/tests/test402/ch12/12.2/12.2.2_L15.js
js/src/tests/test402/ch12/12.2/12.2.2_a.js
js/src/tests/test402/ch12/12.2/12.2.2_b.js
js/src/tests/test402/ch12/12.2/12.2.3_b.js
js/src/tests/test402/ch12/12.2/12.2.3_c.js
js/src/tests/test402/ch12/12.2/browser.js
js/src/tests/test402/ch12/12.2/shell.js
js/src/tests/test402/ch12/12.3/12.3.1.js
js/src/tests/test402/ch12/12.3/12.3.2_1_a_L15.js
js/src/tests/test402/ch12/12.3/12.3.2_1_c.js
js/src/tests/test402/ch12/12.3/12.3.2_FDT_1.js
js/src/tests/test402/ch12/12.3/12.3.2_FDT_7_a_iv.js
js/src/tests/test402/ch12/12.3/12.3.2_L15.js
js/src/tests/test402/ch12/12.3/12.3.2_TLT_2.js
js/src/tests/test402/ch12/12.3/12.3.3.js
js/src/tests/test402/ch12/12.3/12.3.3_L15.js
js/src/tests/test402/ch12/12.3/12.3_L15.js
js/src/tests/test402/ch12/12.3/12.3_a.js
js/src/tests/test402/ch12/12.3/12.3_b.js
js/src/tests/test402/ch12/12.3/browser.js
js/src/tests/test402/ch12/12.3/shell.js
js/src/tests/test402/ch12/12.4/12.4_a.js
js/src/tests/test402/ch12/12.4/browser.js
js/src/tests/test402/ch12/12.4/shell.js
js/src/tests/test402/ch12/browser.js
js/src/tests/test402/ch12/shell.js
js/src/tests/test402/ch13/13.1/13.1.1_1.js
js/src/tests/test402/ch13/13.1/13.1.1_2.js
js/src/tests/test402/ch13/13.1/13.1.1_3_1.js
js/src/tests/test402/ch13/13.1/13.1.1_3_2.js
js/src/tests/test402/ch13/13.1/13.1.1_6_1.js
js/src/tests/test402/ch13/13.1/13.1.1_6_2.js
js/src/tests/test402/ch13/13.1/13.1.1_7.js
js/src/tests/test402/ch13/13.1/13.1.1_L15.js
js/src/tests/test402/ch13/13.1/browser.js
js/src/tests/test402/ch13/13.1/shell.js
js/src/tests/test402/ch13/13.2/13.2.1_1.js
js/src/tests/test402/ch13/13.2/13.2.1_4_1.js
js/src/tests/test402/ch13/13.2/13.2.1_4_2.js
js/src/tests/test402/ch13/13.2/13.2.1_5.js
js/src/tests/test402/ch13/13.2/13.2.1_L15.js
js/src/tests/test402/ch13/13.2/browser.js
js/src/tests/test402/ch13/13.2/shell.js
js/src/tests/test402/ch13/13.3/13.3.0_1.js
js/src/tests/test402/ch13/13.3/13.3.0_2.js
js/src/tests/test402/ch13/13.3/13.3.0_6_1.js
js/src/tests/test402/ch13/13.3/13.3.0_6_2.js
js/src/tests/test402/ch13/13.3/13.3.0_7.js
js/src/tests/test402/ch13/13.3/13.3.1_L15.js
js/src/tests/test402/ch13/13.3/13.3.2_L15.js
js/src/tests/test402/ch13/13.3/13.3.3_L15.js
js/src/tests/test402/ch13/13.3/browser.js
js/src/tests/test402/ch13/13.3/shell.js
js/src/tests/test402/ch13/browser.js
js/src/tests/test402/ch13/shell.js
js/src/tests/test402/lib/testBuiltInObject.js
js/src/tests/test402/lib/testIntl.js
js/src/tests/test402/shell.js
layout/base/crashtests/794693.html
layout/generic/crashtests/660451-1.html
testing/marionette/client/marionette/marionette_touch.py
testing/marionette/client/marionette/tests/unit/test_marionette_touch.py
testing/marionette/client/marionette/touch/synthetic_gestures.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,12 +12,12 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 869002 touched webidl, so Windows needs to clobber
+Bug 496923 removed a directory which the non-regenerated js/src/tests/Makefile will still request.
 
 Alternative to clobber is to run ./config.status from the objdir and to
 touch the CLOBBER file in the objdir.
--- a/accessible/src/base/nsAccessiblePivot.cpp
+++ b/accessible/src/base/nsAccessiblePivot.cpp
@@ -577,17 +577,18 @@ RuleCache::ApplyFilter(Accessible* aAcce
       return NS_OK;
 
     if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
         !(state & states::FOCUSABLE))
       return NS_OK;
 
     if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) {
       nsIContent* content = aAccessible->GetContent();
-      if (nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) &&
+      if (content &&
+          nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) &&
           !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
                                 nsGkAtoms::_false, eCaseMatters)) {
         *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
         return NS_OK;
       }
     }
   }
 
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1318,19 +1318,16 @@ HyperTextAccessible::NativeAttributes()
       if (tag == nsGkAtoms::header) {
         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                                NS_LITERAL_STRING("banner"));
       } else if (tag == nsGkAtoms::footer) {
         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                                NS_LITERAL_STRING("contentinfo"));
       }
     }
-  } else if (tag == nsGkAtoms::footer) {
-    nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
-                           NS_LITERAL_STRING("contentinfo"));
   } else if (tag == nsGkAtoms::aside) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("complementary"));
   } else if (tag == nsGkAtoms::article) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("article"));
   } else if (tag == nsGkAtoms::main) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
--- a/accessible/src/generic/TableCellAccessible.cpp
+++ b/accessible/src/generic/TableCellAccessible.cpp
@@ -42,17 +42,17 @@ TableCellAccessible::RowHeaderCells(nsTA
 void
 TableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells)
 {
   uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
   TableAccessible* table = Table();
   if (!table)
     return;
 
-  // Move to the left to find row header cells
+  // Move up to find column header cells
   for (uint32_t curRowIdx = rowIdx - 1; curRowIdx < rowIdx; curRowIdx--) {
     Accessible* cell = table->CellAt(curRowIdx, colIdx);
     if (!cell)
       continue;
 
     // CellAt should always return a TableCellAccessible (XXX Bug 587529)
     TableCellAccessible* tableCell = cell->AsTableCell();
     NS_ASSERTION(tableCell, "cell should be a table cell!");
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -399,17 +399,17 @@ HTMLTextFieldAccessible::NativeState()
   Accessible* widget = ContainerWidget();
   if (widget && widget-IsAutoComplete()) {
     state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
     return state;
   }
 
   // Expose autocomplete state if it has associated autocomplete list.
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
-    return state | states::SUPPORTS_AUTOCOMPLETION;
+    return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
 
   // No parent can mean a fake widget created for XUL textbox. If accessible
   // is unattached from tree then we don't care.
   if (mParent && Preferences::GetBool("browser.formfill.enable")) {
     // Check to see if autocompletion is allowed on this input. We don't expose
     // it for password fields even though the entire password can be remembered
     // for a page if the user asks it to be. However, the kind of autocomplete
     // we're talking here is based on what the user types, where a popup of
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -208,31 +208,49 @@ HTMLTableCellAccessible::RowExtent() con
 
   return table->RowExtentAt(rowIdx, colIdx);
 }
 
 void
 HTMLTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells)
 {
   IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers);
-  while (Accessible* cell = itr.Next())
-    if (cell->Role() == roles::COLUMNHEADER)
+  while (Accessible* cell = itr.Next()) {
+    a11y::role cellRole = cell->Role();
+    if (cellRole == roles::COLUMNHEADER) {
       aCells->AppendElement(cell);
+    } else if (cellRole != roles::ROWHEADER) {
+      // If referred table cell is at the same column then treat it as a column
+      // header.
+      TableCellAccessible* tableCell = cell->AsTableCell();
+      if (tableCell && tableCell->ColIdx() == ColIdx())
+        aCells->AppendElement(cell);
+    }
+  }
 
   if (aCells->IsEmpty())
     TableCellAccessible::ColHeaderCells(aCells);
 }
 
 void
 HTMLTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells)
 {
   IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers);
-  while (Accessible* cell = itr.Next())
-    if (cell->Role() == roles::ROWHEADER)
+  while (Accessible* cell = itr.Next()) {
+    a11y::role cellRole = cell->Role();
+    if (cellRole == roles::ROWHEADER) {
       aCells->AppendElement(cell);
+    } else if (cellRole != roles::COLUMNHEADER) {
+      // If referred table cell is at the same row then treat it as a column
+      // header.
+      TableCellAccessible* tableCell = cell->AsTableCell();
+      if (tableCell && tableCell->RowIdx() == RowIdx())
+        aCells->AppendElement(cell);
+    }
+  }
 
   if (aCells->IsEmpty())
     TableCellAccessible::RowHeaderCells(aCells);
 }
 
 bool
 HTMLTableCellAccessible::Selected()
 {
@@ -289,37 +307,37 @@ HTMLTableHeaderCellAccessible::NativeRol
 
   switch (valueIdx) {
     case 0:
       return roles::COLUMNHEADER;
     case 1:
       return roles::ROWHEADER;
   }
 
-  // Assume it's columnheader if there are headers in siblings, oterwise
+  // Assume it's columnheader if there are headers in siblings, otherwise
   // rowheader.
   nsIContent* parentContent = mContent->GetParent();
   if (!parentContent) {
     NS_ERROR("Deattached content on alive accessible?");
     return roles::NOTHING;
   }
 
   for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent;
        siblingContent = siblingContent->GetPreviousSibling()) {
     if (siblingContent->IsElement()) {
       return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-	     roles::COLUMNHEADER : roles::ROWHEADER;
+        roles::COLUMNHEADER : roles::ROWHEADER;
     }
   }
 
   for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent;
        siblingContent = siblingContent->GetNextSibling()) {
     if (siblingContent->IsElement()) {
       return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-	     roles::COLUMNHEADER : roles::ROWHEADER;
+       roles::COLUMNHEADER : roles::ROWHEADER;
     }
   }
 
   // No elements in siblings what means the table has one column only. Therefore
   // it should be column header.
   return roles::COLUMNHEADER;
 }
 
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -189,16 +189,19 @@ this.AccessFu = {
         }
         break;
       case 'AccessFu:Present':
         this._output(aMessage.json, aMessage.target);
         break;
       case 'AccessFu:Input':
         this.Input.setEditState(aMessage.json);
         break;
+      case 'AccessFu:ActivateContextMenu':
+        this.Input.activateContextMenu(aMessage.json);
+        break;
     }
   },
 
   _output: function _output(aPresentationData, aBrowser) {
     for each (let presenter in aPresentationData) {
       if (!presenter)
         continue;
 
@@ -227,22 +230,24 @@ this.AccessFu = {
         {method: 'start', buildApp: Utils.MozBuildApp});
     }
   },
 
   _addMessageListeners: function _addMessageListeners(aMessageManager) {
     aMessageManager.addMessageListener('AccessFu:Present', this);
     aMessageManager.addMessageListener('AccessFu:Input', this);
     aMessageManager.addMessageListener('AccessFu:Ready', this);
+    aMessageManager.addMessageListener('AccessFu:ActivateContextMenu', this);
   },
 
   _removeMessageListeners: function _removeMessageListeners(aMessageManager) {
     aMessageManager.removeMessageListener('AccessFu:Present', this);
     aMessageManager.removeMessageListener('AccessFu:Input', this);
     aMessageManager.removeMessageListener('AccessFu:Ready', this);
+    aMessageManager.removeMessageListener('AccessFu:ActivateContextMenu', this);
   },
 
   _handleMessageManager: function _handleMessageManager(aMessageManager) {
     if (this._enabled) {
       this._addMessageListeners(aMessageManager);
     }
     this._loadFrameScript(aMessageManager);
   },
@@ -527,16 +532,19 @@ var Input = {
       case 'dwell1':
       case 'explore1':
         this.moveCursor('moveToPoint', 'Simple', 'gesture',
                         aGesture.x, aGesture.y);
         break;
       case 'doubletap1':
         this.activateCurrent();
         break;
+      case 'doubletaphold1':
+        this.sendContextMenuMessage();
+        break;
       case 'swiperight1':
         this.moveCursor('moveNext', 'Simple', 'gestures');
         break;
       case 'swipeleft1':
         this.moveCursor('movePrevious', 'Simple', 'gesture');
         break;
       case 'swiperight2':
         this.scroll(-1, true);
@@ -653,16 +661,27 @@ var Input = {
                          inputType: aInputType});
   },
 
   activateCurrent: function activateCurrent() {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
     mm.sendAsyncMessage('AccessFu:Activate', {});
   },
 
+  sendContextMenuMessage: function sendContextMenuMessage() {
+    let mm = Utils.getMessageManager(Utils.CurrentBrowser);
+    mm.sendAsyncMessage('AccessFu:ContextMenu', {});
+  },
+
+  activateContextMenu: function activateContextMenu(aMessage) {
+    if (Utils.MozBuildApp === 'mobile/android')
+      Services.obs.notifyObservers(null, 'Gesture:LongPress',
+                                   JSON.stringify({x: aMessage.x, y: aMessage.y}));
+  },
+
   setEditState: function setEditState(aEditState) {
     this.editState = aEditState;
   },
 
   scroll: function scroll(aPage, aHorizontal) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
     mm.sendAsyncMessage('AccessFu:Scroll', {page: aPage, horizontal: aHorizontal, origin: 'top'});
   },
--- a/accessible/src/jsat/TouchAdapter.jsm
+++ b/accessible/src/jsat/TouchAdapter.jsm
@@ -222,16 +222,19 @@ this.TouchAdapter = {
           let sequence = prevGesture.type + '-' + details.type;
           switch (sequence) {
             case 'tap-tap':
               details.type = 'doubletap';
               break;
             case 'doubletap-tap':
               details.type = 'tripletap';
               break;
+            case 'doubletap-dwell':
+              details.type = 'doubletaphold';
+              break;
             case 'tap-dwell':
               details.type = 'taphold';
               break;
             case 'explore-explore':
               details.deltaX = details.x - prevGesture.x;
               details.deltaY = details.y - prevGesture.y;
               break;
           }
@@ -255,16 +258,19 @@ this.TouchAdapter = {
     // to single taps.
     if (Utils.MozBuildApp == 'mobile/android' &&
         Utils.AndroidSdkVersion >= 14 &&
         aDetails.touches[0] != this.MOUSE_ID) {
       if (aDetails.touches.length == 1) {
         if (aDetails.type == 'tap') {
           emitDelay = 50;
           aDetails.type = 'doubletap';
+        } else if (aDetails.type == 'dwell') {
+          emitDelay = 50;
+          aDetails.type = 'doubletaphold';
         } else {
           aDetails.touches.push(this.MOUSE_ID);
         }
       }
     }
 
     let emit = function emit() {
       let evt = Utils.win.document.createEvent('CustomEvent');
--- a/accessible/src/jsat/content-script.js
+++ b/accessible/src/jsat/content-script.js
@@ -151,16 +151,30 @@ function activateCurrent(aMessage) {
     }
   }
 
   let vc = Utils.getVirtualCursor(content.document);
   if (!forwardMessage(vc, aMessage))
     activateAccessible(vc.position);
 }
 
+function activateContextMenu(aMessage) {
+  function sendContextMenuCoordinates(aAccessible) {
+    let objX = {}, objY = {}, objW = {}, objH = {};
+    aAccessible.getBounds(objX, objY, objW, objH);
+    let x = objX.value + objW.value / 2;
+    let y = objY.value + objH.value / 2;
+    sendAsyncMessage('AccessFu:ActivateContextMenu', {x: x, y: y});
+  }
+
+  let vc = Utils.getVirtualCursor(content.document);
+  if (!forwardMessage(vc, aMessage))
+    sendContextMenuCoordinates(vc.position);
+}
+
 function scroll(aMessage) {
   let vc = Utils.getVirtualCursor(content.document);
 
   function tryToScroll() {
     let horiz = aMessage.json.horizontal;
     let page = aMessage.json.page;
 
     // Search up heirarchy for scrollable element.
@@ -243,29 +257,31 @@ addMessageListener(
   'AccessFu:Start',
   function(m) {
     Logger.debug('AccessFu:Start');
     if (m.json.buildApp)
       Utils.MozBuildApp = m.json.buildApp;
 
     addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
     addMessageListener('AccessFu:Activate', activateCurrent);
+    addMessageListener('AccessFu:ContextMenu', activateContextMenu);
     addMessageListener('AccessFu:Scroll', scroll);
 
     if (!eventManager) {
       eventManager = new EventManager(this);
     }
     eventManager.start();
   });
 
 addMessageListener(
   'AccessFu:Stop',
   function(m) {
     Logger.debug('AccessFu:Stop');
 
     removeMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
     removeMessageListener('AccessFu:Activate', activateCurrent);
+    removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
     removeMessageListener('AccessFu:Scroll', scroll);
 
     eventManager.stop();
   });
 
 sendAsyncMessage('AccessFu:Ready');
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -914,16 +914,19 @@ AccessibleWrap::accNavigate(
       xpRelation = nsIAccessibleRelation::RELATION_DEFAULT_BUTTON;
       break;
     case NAVRELATION_DESCRIBED_BY:
       xpRelation = nsIAccessibleRelation::RELATION_DESCRIBED_BY;
       break;
     case NAVRELATION_DESCRIPTION_FOR:
       xpRelation = nsIAccessibleRelation::RELATION_DESCRIPTION_FOR;
       break;
+    case NAVRELATION_NODE_PARENT_OF:
+      xpRelation = nsIAccessibleRelation::RELATION_NODE_PARENT_OF;
+      break;
 
     default:
       return E_INVALIDARG;
   }
 
   pvarEndUpAt->vt = VT_EMPTY;
 
   if (xpRelation) {
@@ -1246,16 +1249,18 @@ AccessibleWrap::get_states(AccessibleSta
   if (state & states::STALE)
     *aStates |= IA2_STATE_STALE;
   if (state & states::SUPPORTS_AUTOCOMPLETION)
     *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
   if (state & states::TRANSIENT)
     *aStates |= IA2_STATE_TRANSIENT;
   if (state & states::VERTICAL)
     *aStates |= IA2_STATE_VERTICAL;
+  if (state & states::CHECKED)
+    *aStates |= IA2_STATE_CHECKABLE;
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::get_extendedRole(BSTR *aExtendedRole)
--- a/accessible/src/windows/msaa/AccessibleWrap.h
+++ b/accessible/src/windows/msaa/AccessibleWrap.h
@@ -263,16 +263,17 @@ protected:
     NAVRELATION_FLOWS_FROM = 0x1007,
     NAVRELATION_SUBWINDOW_OF = 0x1008,
     NAVRELATION_EMBEDS = 0x1009,
     NAVRELATION_EMBEDDED_BY = 0x100a,
     NAVRELATION_POPUP_FOR = 0x100b,
     NAVRELATION_PARENT_WINDOW_OF = 0x100c,
     NAVRELATION_DEFAULT_BUTTON = 0x100d,
     NAVRELATION_DESCRIBED_BY = 0x100e,
-    NAVRELATION_DESCRIPTION_FOR = 0x100f
+    NAVRELATION_DESCRIPTION_FOR = 0x100f,
+    NAVRELATION_NODE_PARENT_OF = 0x1010
   };
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/tests/mochitest/jsat/test_utterance_order.html
+++ b/accessible/tests/mochitest/jsat/test_utterance_order.html
@@ -81,16 +81,41 @@ https://bugzilla.mozilla.org/show_bug.cg
             ["list 4 items", "First item", "link", "Apples"],
             ["Apples", "link", "First item", "list 4 items"]
           ]
         }, {
           // Test pivot to 'bananas' link from 'apples' link.
           accOrElmOrID: "bananas",
           oldAccOrElmOrID: "apples",
           expected: [["link", "Bananas"], ["Bananas", "link"]]
+        }, {
+          // test unavailable state utterance
+          accOrElmOrID: 'unavailableButton',
+          expected: [["unavailable button", "I am unavailable"],
+            ["I am unavailable", "unavailable button"]]
+        }, {
+          // test expanded state utterance
+          accOrElmOrID: 'expandedButton',
+          expected: [["expanded button", "I am expanded"],
+            ["I am expanded", "expanded button"]]
+        }, {
+          // test collapsed state utterance
+          accOrElmOrID: 'collapsedButton',
+          expected: [["collapsed button", "I am collapsed"],
+            ["I am collapsed", "collapsed button"]]
+        }, {
+          // test required state utterance
+          accOrElmOrID: 'requiredInput',
+          expected: [["required entry", "I am required"],
+            ["I am required", "required entry"]]
+        }, {
+          // test has popup state utterance
+          accOrElmOrID: 'hasPopupButton',
+          expected: [["has pop up button menu", "I have a popup"],
+            ["I have a popup", "has pop up button menu"]]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -144,11 +169,16 @@ https://bugzilla.mozilla.org/show_bug.cg
                 <a href="#">
                   Plums
                 </a>
               </li>
             </ul>
           </td>
         </tr>
       </table>
+      <button id="unavailableButton" disabled>I am unavailable</button>
+      <button id="expandedButton" aria-expanded="true">I am expanded</button>
+      <button id="collapsedButton" aria-expanded="false">I am collapsed</button>
+      <input id="requiredInput" required placeholder="I am required" />
+      <button id="hasPopupButton" aria-haspopup="true">I have a popup</button>
     </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/states/test_inputs.html
+++ b/accessible/tests/mochitest/states/test_inputs.html
@@ -106,64 +106,74 @@
     testStates("autocomplete-default", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-off", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-formoff", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-list", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-list2", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-tel", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-email", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
     testStates("autocomplete-search", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+
+    ////////////////////////////////////////////////////////////////////////////
+    // haspopup
+    testStates("autocomplete-list", STATE_HASPOPUP);
+
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
   addA11yLoadEvent(doTest);
   </script>
 </head>
 
 <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=559275"
      title="map attribute required to STATE_REQUIRED">
-    Mozilla Bug 559275
+    Bug 559275
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=389238"
      title="Support disabled state on fieldset">
-    Mozilla Bug 389238
+    Bug 389238
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=599163"
      title="check disabled state instead of attribute">
-    Mozilla Bug 599163
+    Bug 599163
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=601205"
+     title="Expose intrinsic invalid state to accessibility API">
+    Bug 601205
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=601205"
      title="Expose intrinsic invalid state to accessibility API">
-    Mozilla Bug 601205
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=601205"
-     title="Expose intrinsic invalid state to accessibility API">
-    Mozilla Bug 601205
+    Bug 601205
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=559766"
      title="Add accessibility support for @list on HTML input and for HTML datalist">
-    Mozilla Bug 559766
+    Bug 559766
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
      title="File input control should be propogate states to descendants">
-    Mozilla Bug 699017
+    Bug 699017
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=733382"
      title="Editable state bit should be present on readonly inputs">
-    Mozilla Bug 733382
+    Bug 733382
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=878590"
+     title="HTML5 datalist is not conveyed by haspopup property">
+    Bug 878590
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
 
--- a/accessible/tests/mochitest/table/test_headers_table.html
+++ b/accessible/tests/mochitest/table/test_headers_table.html
@@ -138,28 +138,48 @@
           cell: "table5_cell",
           rowHeaderCells: [ "table5_rh" ],
           columnHeaderCells: [ ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
+      //////////////////////////////////////////////////////////////////////////
+      // @headers points to table cells
+
+      headerInfoMap = [
+        {
+          cell: "table6_cell",
+          rowHeaderCells: [ "table6_rh" ],
+          columnHeaderCells: [ "table6_ch" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
 <body>
   <a target="_blank"
      title="implement IAccessibleTable2"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">Mozilla Bug 512424</a>
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">
+    Bug 512424
+  </a>
+  <a target="_blank"
+     title="Table headers not associated when header is a td element with no scope"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=704465">
+    Bug 704465
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <table id="table1" border="1">
     <thead>
@@ -239,10 +259,21 @@
   </table>
 
   <table id="table5">
     <tr>
       <th id="table5_rh">rowheader</th>
       <td id="table5_cell">cell</td>
     </tr>
   </table>
+
+  <table id="table6">
+    <tr>
+      <td>empty cell</th>
+      <td id="table6_ch">colheader</td>
+    </tr>
+    <tr>
+      <td id="table6_rh">rowheader</th>
+      <td id="table6_cell" headers="table6_ch table6_rh">cell</td>
+    </tr>
+  </table>
 </body>
 </html>
--- a/b2g/chrome/content/content.css
+++ b/b2g/chrome/content/content.css
@@ -56,126 +56,125 @@ xul|scrollbarbutton {
 }
 
 xul|scrollbarbutton[sbattr="scrollbar-up-top"],
 xul|scrollbarbutton[sbattr="scrollbar-bottom-top"] {
   display: none;
 }
 
 xul|thumb {
-  -moz-appearance: none !important;
   background-color: rgba(0, 0, 0, 0.4) !important;
   -moz-border-top-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-left-colors: none !important;
   border: 1px solid rgba(255, 255, 255, 0.4) !important;
   border-radius: 3px;
 }
 
 /* -moz-touch-enabled? media elements */
 :-moz-any(video, audio) > xul|videocontrols {
   -moz-binding: url("chrome://global/content/bindings/videocontrols.xml#touchControls");
 }
 
-html select:not([size]):not([multiple]) > xul|scrollbar,
-html select[size="1"] > xul|scrollbar,
-html select:not([size]):not([multiple]) xul|scrollbarbutton,
-html select[size="1"] xul|scrollbarbutton {
+select:not([size]):not([multiple]) > xul|scrollbar,
+select[size="1"] > xul|scrollbar,
+select:not([size]):not([multiple]) xul|scrollbarbutton,
+select[size="1"] xul|scrollbarbutton {
   display: block;
   margin-left: 0;
   min-width: 16px;
 }
 
 /* Override inverse OS themes */
-html select,
-html textarea,
-html button,
-html xul|button,
-html * > input:not([type="image"]) {
+select,
+textarea,
+button,
+xul|button,
+* > input:not([type="image"]) {
   -moz-appearance: none !important;  /* See bug 598421 for fixing the platform */
   border-radius: 3px;
 }
 
-html select[size],
-html select[multiple],
-html select[size][multiple],
-html textarea,
-html * > input:not([type="image"]) {
+select[size],
+select[multiple],
+select[size][multiple],
+textarea,
+* > input:not([type="image"]) {
   border-style: solid;
   border-color: #7d7d7d;
   color: #414141;
   background: white linear-gradient(rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 3px, rgba(255,255,255,0.2) 16px);
 }
 
 /* Selects are handled by the form helper, see bug 685197 */
-html select option, html select optgroup {
+select option, select optgroup {
   pointer-events: none;
 }
 
-html select:not([size]):not([multiple]),
-html select[size="0"],
-html select[size="1"],
-html * > input[type="button"],
-html * > input[type="submit"],
-html * > input[type="reset"],
-html button {
+select:not([size]):not([multiple]),
+select[size="0"],
+select[size="1"],
+* > input[type="button"],
+* > input[type="submit"],
+* > input[type="reset"],
+button {
   border-style: solid;
   border-color: #7d7d7d;
   color: #414141;
   background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(215,215,215,0.5) 18px, rgba(115,115,115,0.5) 100%);
 }
 
-html input[type="checkbox"] {
+input[type="checkbox"] {
   background: white linear-gradient(rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 2px, rgba(255,255,255,0.2) 6px);
 }
 
-html input[type="radio"] {
+input[type="radio"] {
   background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(195,195,195,0.5) 5px, rgba(115,115,115,0.5) 100%);
 }
 
-html select {
+select {
   border-width: 1px;
   padding: 1px;
 }
 
-html select:not([size]):not([multiple]),
-html select[size="0"],
-html select[size="1"] {
+select:not([size]):not([multiple]),
+select[size="0"],
+select[size="1"] {
   padding: 0 1px 0 1px;
 }
 
-html * > input:not([type="image"]) {
+* > input:not([type="image"]) {
   border-width: 1px;
   padding: 1px;
 }
 
-html textarea {
+textarea {
   resize: none;
   border-width: 1px;
   padding: 2px 1px 2px 1px;
 }
 
-html input[type="button"],
-html input[type="submit"],
-html input[type="reset"],
-html button {
+input[type="button"],
+input[type="submit"],
+input[type="reset"],
+button {
   border-width: 1px;
   padding: 0 7px 0 7px;
 }
 
-html input[type="radio"],
-html input[type="checkbox"] {
+input[type="radio"],
+input[type="checkbox"] {
   max-width: 14px;
   max-height: 14px;
   border: 1px solid #a7a7a7 !important;
   padding: 2px 1px 2px 1px;
 }
 
-html select > button {
+select > button {
   border-width: 0px !important;
   margin: 0px !important;
   padding: 0px !important;
   border-radius: 0;
   color: #414141;
 
   background-image: radial-gradient(at bottom left, #bbbbbb 40%, #f5f5f5), url(arrow.svg) !important;
   background-color: transparent;
@@ -183,109 +182,109 @@ html select > button {
   background-repeat: no-repeat, no-repeat !important;
   background-size: 100% 90%, auto auto;
 
   -moz-binding: none !important;
   position: relative !important;
   font-size: inherit;
 }
 
-html select[size]:focus,
-html select[multiple]:focus,
-html select[size][multiple]:focus,
-html textarea:focus,
-html input[type="file"]:focus > input[type="text"],
-html * > input:not([type="image"]):focus {
+select[size]:focus,
+select[multiple]:focus,
+select[size][multiple]:focus,
+textarea:focus,
+input[type="file"]:focus > input[type="text"],
+* > input:not([type="image"]):focus {
   outline: 0px !important;
   border-style: solid;
   border-color: rgb(94,128,153);
   background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px);
 }
 
-html select:not([size]):not([multiple]):focus,
-html select[size="0"]:focus,
-html select[size="1"]:focus,
-html input[type="button"]:focus,
-html input[type="submit"]:focus,
-html input[type="reset"]:focus,
-html button:focus {
+select:not([size]):not([multiple]):focus,
+select[size="0"]:focus,
+select[size="1"]:focus,
+input[type="button"]:focus,
+input[type="submit"]:focus,
+input[type="reset"]:focus,
+button:focus {
   outline: 0px !important;
   border-style: solid;
   border-color: rgb(94,128,153);
   background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(198,225,256,0.2) 18px, rgba(27,113,177,0.5) 100%);
 }
 
-html input[type="checkbox"]:focus,
-html input[type="radio"]:focus {
+input[type="checkbox"]:focus,
+input[type="radio"]:focus {
   border-color: #99c6e0 !important;
 }
 
-html input[type="checkbox"]:focus {
+input[type="checkbox"]:focus {
   background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 2px, rgba(255,255,255,0.2) 6px);
 }
 
-html input[type="radio"]:focus {
+input[type="radio"]:focus {
   background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(198,225,246,0.2) 5px, rgba(27,113,177,0.5) 100%);
 }
 
 /* we need to be specific for selects because the above rules are specific too */
-html textarea[disabled],
-html select[size][disabled],
-html select[multiple][disabled],
-html select[size][multiple][disabled],
-html select:not([size]):not([multiple])[disabled],
-html select[size="0"][disabled],
-html select[size="1"][disabled],
-html button[disabled],
-html * > input:not([type="image"])[disabled] {
+textarea[disabled],
+select[size][disabled],
+select[multiple][disabled],
+select[size][multiple][disabled],
+select:not([size]):not([multiple])[disabled],
+select[size="0"][disabled],
+select[size="1"][disabled],
+button[disabled],
+* > input:not([type="image"])[disabled] {
   color: rgba(0,0,0,0.3);
   border-color: rgba(125,125,125,0.4);
   border-style: solid;
   border-width: 1px;
   background: transparent linear-gradient(rgba(185,185,185,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(255,255,255,0.4) 100%);
 }
 
-html select:not([size]):not([multiple])[disabled],
-html select[size="0"][disabled],
-html select[size="1"][disabled] {
+select:not([size]):not([multiple])[disabled],
+select[size="0"][disabled],
+select[size="1"][disabled] {
   background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
 }
 
-html input[type="button"][disabled],
-html input[type="submit"][disabled],
-html input[type="reset"][disabled],
-html button[disabled="true"] {
+input[type="button"][disabled],
+input[type="submit"][disabled],
+input[type="reset"][disabled],
+button[disabled="true"] {
   padding: 0 7px 0 7px;
   background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
 }
 
-html input[type="radio"][disabled],
-html input[type="radio"][disabled]:active,
-html input[type="radio"][disabled]:hover,
-html input[type="radio"][disabled]:hover:active,
-html input[type="checkbox"][disabled],
-html input[type="checkbox"][disabled]:active,
-html input[type="checkbox"][disabled]:hover,
-html input[type="checkbox"][disabled]:hover:active {
+input[type="radio"][disabled],
+input[type="radio"][disabled]:active,
+input[type="radio"][disabled]:hover,
+input[type="radio"][disabled]:hover:active,
+input[type="checkbox"][disabled],
+input[type="checkbox"][disabled]:active,
+input[type="checkbox"][disabled]:hover,
+input[type="checkbox"][disabled]:hover:active {
   border:1px solid rgba(125,125,125,0.4) !important;
 }
 
-html select[disabled] > button {
+select[disabled] > button {
   opacity: 0.6;
   padding: 1px 7px 1px 7px;
 }
 
-html *:-moz-any-link:active,
-html *[role=button]:active,
-html button:active,
-html input:active,
-html option:active,
-html select:active,
-html label:active,
-html textarea:active {
+*:-moz-any-link:active,
+*[role=button]:active,
+button:active,
+input:active,
+option:active,
+select:active,
+label:active,
+textarea:active {
   background-color: rgba(141, 184, 216, 0.5);
 }
 
 %ifdef MOZ_WIDGET_GONK
 /* This binding only provide key shortcuts that we can't use on devices */
 input,
 textarea {
 -moz-binding: none !important;
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -188,16 +188,17 @@ let FormAssistant = {
     addEventListener("beforeunload", this, true, false);
     addEventListener("input", this, true, false);
     addEventListener("keydown", this, true, false);
     addEventListener("keyup", this, true, false);
     addMessageListener("Forms:Select:Choice", this);
     addMessageListener("Forms:Input:Value", this);
     addMessageListener("Forms:Select:Blur", this);
     addMessageListener("Forms:SetSelectionRange", this);
+    addMessageListener("Forms:ReplaceSurroundingText", this);
   },
 
   ignoredInputTypes: new Set([
     'button', 'file', 'checkbox', 'radio', 'reset', 'submit', 'image'
   ]),
 
   isKeyboardOpened: false,
   selectionStart: -1,
@@ -253,16 +254,21 @@ let FormAssistant = {
 
     this.focusedElement = element;
   },
 
   get documentEncoder() {
     return this._documentEncoder;
   },
 
+  // Get the nsIPlaintextEditor object of current input field.
+  get editor() {
+    return this._editor;
+  },
+
   // Implements nsIEditorObserver get notification when the text content of
   // current input field has changed.
   EditAction: function fa_editAction() {
     if (this._editing) {
       this._editing = false;
       return;
     }
     this.sendKeyboardState(this.focusedElement);
@@ -424,16 +430,25 @@ let FormAssistant = {
 
       case "Forms:SetSelectionRange":  {
         let start = json.selectionStart;
         let end =  json.selectionEnd;
         setSelectionRange(target, start, end);
         this.updateSelection();
         break;
       }
+
+      case "Forms:ReplaceSurroundingText": {
+        let text = json.text;
+        let beforeLength = json.beforeLength;
+        let afterLength = json.afterLength;
+        replaceSurroundingText(target, text, this.selectionStart, beforeLength,
+                               afterLength);
+        break;
+      }
     }
     this._editing = false;
 
   },
 
   showKeyboard: function fa_showKeyboard(target) {
     if (this.isKeyboardOpened)
       return;
@@ -779,8 +794,42 @@ function getPlaintextEditor(element) {
       editor = editingSession.getEditorForWindow(win);
     }
   }
   if (editor) {
     editor.QueryInterface(Ci.nsIPlaintextEditor);
   }
   return editor;
 }
+
+function replaceSurroundingText(element, text, selectionStart, beforeLength,
+                                afterLength) {
+  let editor = FormAssistant.editor;
+  if (!editor) {
+    return;
+  }
+
+  // Check the parameters.
+  if (beforeLength < 0) {
+    beforeLength = 0;
+  }
+  if (afterLength < 0) {
+    afterLength = 0;
+  }
+
+  let start = selectionStart - beforeLength;
+  let end = selectionStart + afterLength;
+
+  if (beforeLength != 0 || afterLength != 0) {
+    // Change selection range before replacing.
+    setSelectionRange(element, start, end);
+  }
+
+  if (start != end) {
+    // Delete the selected text.
+    editor.deleteSelection(Ci.nsIEditor.ePrevious, Ci.nsIEditor.eStrip);
+  }
+
+  if (text) {
+    // Insert the text to be replaced with.
+    editor.insertText(text);
+  }
+}
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -17,82 +17,109 @@ Cu.import("resource://gre/modules/XPCOMU
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
+#ifdef MOZ_B2G_RIL
+XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
+                                   "@mozilla.org/ril/content-helper;1",
+                                   "nsIMobileConnectionProvider");
+#endif
+
+
 const kClosePaymentFlowEvent = "close-payment-flow-dialog";
 
-let requestId;
+let _requestId;
+
+let PaymentProvider = {
+
+  __exposedProps__: {
+    paymentSuccess: 'r',
+    paymentFailed: 'r',
+    iccIds: 'r'
+  },
 
-function paymentSuccess(aResult) {
-  closePaymentFlowDialog(function notifySuccess() {
-    if (!requestId) {
-      return;
-    }
-    cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
-                                               requestId: requestId });
-  });
-}
+  _closePaymentFlowDialog: function _closePaymentFlowDialog(aCallback) {
+    // After receiving the payment provider confirmation about the
+    // successful or failed payment flow, we notify the UI to close the
+    // payment flow dialog and return to the caller application.
+    let id = kClosePaymentFlowEvent + "-" + uuidgen.generateUUID().toString();
 
-function paymentFailed(aErrorMsg) {
-  closePaymentFlowDialog(function notifyError() {
-    if (!requestId) {
+    let browser = Services.wm.getMostRecentWindow("navigator:browser");
+    let content = browser.getContentWindow();
+    if (!content) {
       return;
     }
-    cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
-                                              requestId: requestId });
-  });
-}
+
+    let detail = {
+      type: kClosePaymentFlowEvent,
+      id: id,
+      requestId: _requestId
+    };
 
-function closePaymentFlowDialog(aCallback) {
-  // After receiving the payment provider confirmation about the
-  // successful or failed payment flow, we notify the UI to close the
-  // payment flow dialog and return to the caller application.
-  let randomId = uuidgen.generateUUID().toString();
-  let id = kClosePaymentFlowEvent + "-" + randomId;
+    // In order to avoid race conditions, we wait for the UI to notify that
+    // it has successfully closed the payment flow and has recovered the
+    // caller app, before notifying the parent process to fire the success
+    // or error event over the DOMRequest.
+    content.addEventListener("mozContentEvent",
+                             function closePaymentFlowReturn(evt) {
+      if (evt.detail.id == id && aCallback) {
+        aCallback();
+      }
 
-  let browser = Services.wm.getMostRecentWindow("navigator:browser");
-  let content = browser.getContentWindow();
-  if (!content) {
-    return;
-  }
+      content.removeEventListener("mozContentEvent",
+                                  closePaymentFlowReturn);
+
+      let glue = Cc["@mozilla.org/payment/ui-glue;1"]
+                   .createInstance(Ci.nsIPaymentUIGlue);
+      glue.cleanup();
+    });
+
+    browser.shell.sendChromeEvent(detail);
+  },
 
-  let detail = {
-    type: kClosePaymentFlowEvent,
-    id: id,
-    requestId: requestId
-  };
+  paymentSuccess: function paymentSuccess(aResult) {
+    this._closePaymentFlowDialog(function notifySuccess() {
+      if (!_requestId) {
+        return;
+      }
+      cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
+                                                 requestId: _requestId });
+    });
+  },
 
-  // In order to avoid race conditions, we wait for the UI to notify that
-  // it has successfully closed the payment flow and has recovered the
-  // caller app, before notifying the parent process to fire the success
-  // or error event over the DOMRequest.
-  content.addEventListener("mozContentEvent",
-                           function closePaymentFlowReturn(evt) {
-    if (evt.detail.id == id && aCallback) {
-      aCallback();
-    }
+  paymentFailed: function paymentFailed(aErrorMsg) {
+    this._closePaymentFlowDialog(function notifyError() {
+      if (!_requestId) {
+        return;
+      }
+      cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
+                                                requestId: _requestId });
+    });
+  },
 
-    content.removeEventListener("mozContentEvent",
-                                closePaymentFlowReturn);
+  get iccIds() {
+#ifdef MOZ_B2G_RIL
+    // Until bug 814629 is done, we only have support for a single SIM, so we
+    // can only provide a single ICC ID. However, we return an array so the
+    // payment provider facing API won't need to change once we support
+    // multiple SIMs.
+    return [mobileConnection.iccInfo.iccid];
+#else
+    return null;
+#endif
+  },
 
-    let glue = Cc["@mozilla.org/payment/ui-glue;1"]
-                 .createInstance(Ci.nsIPaymentUIGlue);
-    glue.cleanup();
-  });
-
-  browser.shell.sendChromeEvent(detail);
-}
+};
 
 // We save the identifier of the DOM request, so we can dispatch the results
 // of the payment flow to the appropriate content process.
 addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
-  requestId = aMessage.json.requestId;
+  _requestId = aMessage.json.requestId;
 });
 
 addEventListener("DOMWindowCreated", function(e) {
-  content.wrappedJSObject.paymentSuccess = paymentSuccess;
-  content.wrappedJSObject.paymentFailed = paymentFailed;
+  content.wrappedJSObject.mozPaymentProvider = PaymentProvider;
 });
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -16,17 +16,17 @@ chrome.jar:
 * content/shell.js                      (content/shell.js)
 #ifndef ANDROID
   content/screen.js                     (content/screen.js)
   content/runapp.js                     (content/runapp.js)
 #endif
 * content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
-  content/payment.js                    (content/payment.js)
+* content/payment.js                    (content/payment.js)
   content/identity.js                   (content/identity.js)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
 % override chrome://global/skin/netError.css chrome://browser/content/netError.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css
 % override chrome://global/content/aboutCertError.xhtml chrome://browser/content/aboutCertError.xhtml
 
   content/ErrorPage.js                  (content/ErrorPage.js)
--- a/b2g/components/Keyboard.jsm
+++ b/b2g/components/Keyboard.jsm
@@ -16,17 +16,17 @@ Cu.import("resource://gre/modules/XPCOMU
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
   "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
 
 let Keyboard = {
   _messageManager: null,
   _messageNames: [
     'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions',
-    'SetSelectionRange'
+    'SetSelectionRange', 'ReplaceSurroundingText'
   ],
 
   get messageManager() {
     if (this._messageManager && !Cu.isDeadWrapper(this._messageManager))
       return this._messageManager;
 
     throw Error('no message manager set');
   },
@@ -111,16 +111,19 @@ let Keyboard = {
         this.setSelectedOption(msg);
         break;
       case 'Keyboard:SetSelectedOptions':
         this.setSelectedOption(msg);
         break;
       case 'Keyboard:SetSelectionRange':
         this.setSelectionRange(msg);
         break;
+      case 'Keyboard:ReplaceSurroundingText':
+        this.replaceSurroundingText(msg);
+        break;
     }
   },
 
   handleFormsInput: function keyboardHandleFormsInput(msg) {
     this.messageManager = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
                              .frameLoader.messageManager;
 
     ppmm.broadcastAsyncMessage('Keyboard:FocusChange', msg.data);
@@ -146,12 +149,17 @@ let Keyboard = {
   },
 
   setValue: function keyboardSetValue(msg) {
     this.messageManager.sendAsyncMessage('Forms:Input:Value', msg.data);
   },
 
   removeFocus: function keyboardRemoveFocus() {
     this.messageManager.sendAsyncMessage('Forms:Select:Blur', {});
+  },
+
+  replaceSurroundingText: function keyboardReplaceSurroundingText(msg) {
+    this.messageManager.sendAsyncMessage('Forms:ReplaceSurroundingText',
+                                         msg.data);
   }
 };
 
 Keyboard.init();
--- a/b2g/components/MozKeyboard.js
+++ b/b2g/components/MozKeyboard.js
@@ -138,16 +138,25 @@ MozKeyboard.prototype = {
   set onfocuschange(val) {
     this._focusHandler = val;
   },
 
   get onfocuschange() {
     return this._focusHandler;
   },
 
+  replaceSurroundingText: function mozKeyboardReplaceSurroundingText(
+    text, beforeLength, afterLength) {
+    cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
+      'text': text || '',
+      'beforeLength': (typeof beforeLength === 'number' ? beforeLength : 0),
+      'afterLength': (typeof afterLength === 'number' ? afterLength: 0)
+    });
+  },
+
   receiveMessage: function mozKeyboardReceiveMessage(msg) {
     if (msg.name == "Keyboard:FocusChange") {
        let msgJson = msg.json;
        if (msgJson.type != "blur") {
          this._selectionStart = msgJson.selectionStart;
          this._selectionEnd = msgJson.selectionEnd;
        } else {
          this._selectionStart = 0;
--- a/b2g/components/b2g.idl
+++ b/b2g/components/b2g.idl
@@ -6,17 +6,17 @@
 
 [scriptable, uuid(3615a616-571d-4194-bf54-ccf546067b14)]
 interface nsIB2GCameraContent : nsISupports
 {
   /* temporary solution, waiting for getUserMedia */
   DOMString getCameraURI([optional] in jsval options);
 };
 
-[scriptable, uuid(94233b76-3987-4fc0-8924-7596846f9bd3)]
+[scriptable, uuid(40ad96b2-9efa-41fb-84c7-fbcec9b153f0)]
 interface nsIB2GKeyboard : nsISupports
 {
   void sendKey(in long keyCode, in long charCode);
 
   // Select the <select> option specified by index.
   // If this method is called on a <select> that support multiple
   // selection, then the option specified by index will be added to
   // the selection.
@@ -29,17 +29,17 @@ interface nsIB2GKeyboard : nsISupports
   // If this method is called for a <select> that does not support multiple
   // selection, then the last index specified in indexes will be selected.
   void setSelectedOptions(in jsval indexes);
 
   // Set the value on the currently focused element. This has to be used
   // for special situations where the value had to be chosen amongst a
   // list (type=month) or a widget (type=date, time, etc.).
   // If the value passed in parameter isn't valid (in the term of HTML5
-  // Forms Validation), the value will simply be ignored by the element. 
+  // Forms Validation), the value will simply be ignored by the element.
   void setValue(in jsval value);
 
   void removeFocus();
 
   attribute nsIDOMEventListener onfocuschange;
 
   // Fires when user moves the cursor, changes the selection, or alters the
   // composing text length
@@ -56,9 +56,23 @@ interface nsIB2GKeyboard : nsISupports
    *
    * @param start The beginning of the selected text.
    * @param end The end of the selected text.
    *
    * Note that the start position should be less or equal to the end position.
    * To move the cursor, set the start and end position to the same value.
    */
   void setSelectionRange(in long start, in long end);
+
+  /*
+   * Replace text around the beginning of the current selection range of the
+   * editable text.
+   *
+   * @param text The string to be replaced with.
+   * @param beforeLength The number of characters to be deleted before the
+   * beginning of the current selection range. Defaults to 0.
+   * @param afterLength The number of characters to be deleted after the
+   * beginning of the current selection range. Defaults to 0.
+   */
+  void replaceSurroundingText(in DOMString text,
+                              [optional] in long beforeLength,
+                              [optional] in long afterLength);
 };
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -466,16 +466,18 @@
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/NetworkStatsManager.js
 @BINPATH@/components/NetworkStatsManager.manifest
+@BINPATH@/components/NetworkInterfaceListService.manifest
+@BINPATH@/components/NetworkInterfaceListService.js
 #endif
 #ifdef MOZ_B2G_FM
 @BINPATH@/components/DOMFMRadioChild.js
 @BINPATH@/components/DOMFMRadio.manifest
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -1008,17 +1008,17 @@ function makePreview(row)
       height = newImage.height;
 
       document.getElementById("theimagecontainer").collapsed = false
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
     else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
       newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
       newImage.id = "thepreviewimage";
-      newImage.mozLoadFrom(item);
+      newImage.src = url;
       newImage.controls = true;
       width = physWidth = item.videoWidth;
       height = physHeight = item.videoHeight;
 
       document.getElementById("theimagecontainer").collapsed = false;
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
     else if (item instanceof HTMLAudioElement && isProtocolAllowed) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1472,17 +1472,17 @@
                 this.tabContainer._handleTabTelemetryStart(t, aURI);
 
                 // kick the animation off
                 t.setAttribute("fadein", "true");
 
                 // This call to adjustTabstrip is redundant but needed so that
                 // when opening a second tab, the first tab's close buttons
                 // appears immediately rather than when the transition ends.
-                if (this.tabContainer.childNodes.length == 2)
+                if (this.tabs.length - this._removingTabs.length == 2)
                   this.tabContainer.adjustTabstrip();
               }.bind(this));
             }
 
             return t;
           ]]>
         </body>
       </method>
@@ -1613,16 +1613,23 @@
             }
 
             this.tabContainer._handleTabTelemetryStart(aTab);
 
             this._blurTab(aTab);
             aTab.style.maxWidth = ""; // ensure that fade-out transition happens
             aTab.removeAttribute("fadein");
 
+            if (this.tabs.length - this._removingTabs.length == 1) {
+              // The second tab just got closed and we will end up with a single
+              // one. Remove the first tab's close button immediately (if needed)
+              // rather than after the tabclose animation ends.
+              this.tabContainer.adjustTabstrip();
+            }
+
             setTimeout(function (tab, tabbrowser) {
               if (tab.parentNode &&
                   window.getComputedStyle(tab).maxWidth == "0.1px") {
                 NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
                 tabbrowser._endRemoveTab(tab);
               }
             }, 3000, aTab, this);
           ]]>
@@ -3158,34 +3165,46 @@
             this.visible = window.toolbar.visible;
           else
             this.visible = true;
         ]]></body>
       </method>
 
       <method name="adjustTabstrip">
         <body><![CDATA[
+          let numTabs = this.childNodes.length -
+                        this.tabbrowser._removingTabs.length;
           // modes for tabstrip
           // 0 - button on active tab only
           // 1 - close buttons on all tabs
           // 2 - no close buttons at all
           // 3 - close button at the end of the tabstrip
           switch (this.mCloseButtons) {
           case 0:
-            if (this.childNodes.length == 1 && this._closeWindowWithLastTab)
+            if (numTabs == 1 && this._closeWindowWithLastTab)
               this.setAttribute("closebuttons", "hidden");
             else
               this.setAttribute("closebuttons", "activetab");
             break;
           case 1:
-            if (this.childNodes.length == 1) {
+            if (numTabs == 1) {
               if (this._closeWindowWithLastTab)
                 this.setAttribute("closebuttons", "hidden");
               else
                 this.setAttribute("closebuttons", "alltabs");
+            } else if (numTabs == 2) {
+              // This is an optimization to avoid layout flushes by calling
+              // getBoundingClientRect() when we just opened a second tab. In
+              // this case it's highly unlikely that the tab width is smaller
+              // than mTabClipWidth and the tab close button obscures too much
+              // of the tab's label. In the edge case of the window being too
+              // narrow (or if tabClipWidth has been set to a way higher value),
+              // we'll correct the 'closebuttons' attribute after the tabopen
+              // animation has finished.
+              this.setAttribute("closebuttons", "alltabs");
             } else {
               let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
               if (tab && tab.getBoundingClientRect().width > this.mTabClipWidth)
                 this.setAttribute("closebuttons", "alltabs");
               else
                 this.setAttribute("closebuttons", "activetab");
             }
             break;
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -195,16 +195,17 @@ endif
                  browser_CTP_drag_drop.js \
                  browser_pluginplaypreview.js \
                  browser_pluginplaypreview2.js \
                  browser_private_browsing_window.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
+                 browser_tabopen_reflows.js \
                  browser_clearplugindata.js \
                  browser_clearplugindata.html \
                  browser_clearplugindata_noage.html \
                  browser_popupUI.js \
                  browser_sanitizeDialog.js \
                  browser_save_link-perwindowpb.js \
                  browser_save_private_link_perwindowpb.js \
                  browser_save_video.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_tabopen_reflows.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+XPCOMUtils.defineLazyGetter(this, "docShell", () => {
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsIDocShell);
+});
+
+const EXPECTED_REFLOWS = [
+  // b.stop() call in tabbrowser.addTab()
+  "stop@chrome://global/content/bindings/browser.xml|" +
+    "addTab@chrome://browser/content/tabbrowser.xml|",
+
+  // tabbrowser.adjustTabstrip() call after tabopen animation has finished
+  "adjustTabstrip@chrome://browser/content/tabbrowser.xml|" +
+    "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
+    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
+
+  // switching focus in updateCurrentBrowser() causes reflows
+  "updateCurrentBrowser@chrome://browser/content/tabbrowser.xml|" +
+    "onselect@chrome://browser/content/browser.xul|",
+
+  // switching focus in openLinkIn() causes reflows
+  "openLinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
+    "BrowserOpenTab@chrome://browser/content/browser.js|",
+
+  // accessing element.scrollPosition in _fillTrailingGap() flushes layout
+  "get_scrollPosition@chrome://global/content/bindings/scrollbox.xml|" +
+    "_fillTrailingGap@chrome://browser/content/tabbrowser.xml|" +
+    "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
+    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|"
+];
+
+/*
+ * This test ensures that there are no unexpected
+ * uninterruptible reflows when opening new tabs.
+ */
+function test() {
+  waitForExplicitFinish();
+
+  // Add a reflow observer and open a new tab.
+  docShell.addWeakReflowObserver(observer);
+  BrowserOpenTab();
+
+  // Wait until the tabopen animation has finished.
+  waitForTransitionEnd(function () {
+    // Remove reflow observer and clean up.
+    docShell.removeWeakReflowObserver(observer);
+    gBrowser.removeCurrentTab();
+
+    finish();
+  });
+}
+
+let observer = {
+  reflow: function (start, end) {
+    // Gather information about the current code path.
+    let path = (new Error().stack).split("\n").slice(1).map(line => {
+      return line.replace(/:\d+$/, "");
+    }).join("|");
+
+    // Stack trace is empty. Reflow was triggered by native code.
+    if (path === "") {
+      return;
+    }
+
+    // Check if this is an expected reflow.
+    for (let stack of EXPECTED_REFLOWS) {
+      if (path.startsWith(stack)) {
+        ok(true, "expected uninterruptible reflow '" + stack + "'");
+        return;
+      }
+    }
+
+    ok(false, "unexpected uninterruptible reflow '" + path + "'");
+  },
+
+  reflowInterruptible: function (start, end) {
+    // We're not interested in interruptible reflows.
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
+                                         Ci.nsISupportsWeakReference])
+};
+
+function waitForTransitionEnd(callback) {
+  let tab = gBrowser.selectedTab;
+  tab.addEventListener("transitionend", function onEnd(event) {
+    if (event.propertyName === "max-width") {
+      tab.removeEventListener("transitionend", onEnd);
+      executeSoon(callback);
+    }
+  });
+}
--- a/browser/components/migration/src/SafariProfileMigrator.js
+++ b/browser/components/migration/src/SafariProfileMigrator.js
@@ -14,16 +14,18 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PropertyListUtils",
                                   "resource://gre/modules/PropertyListUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+                                  "resource://gre/modules/FormHistory.jsm");
 
 function Bookmarks(aBookmarksFile) {
   this._file = aBookmarksFile;
 }
 Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   migrate: function B_migrate(aCallback) {
@@ -535,21 +537,21 @@ SearchStrings.prototype = {
     this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
       function migrateSearchStrings(aDict) {
         if (!aDict)
           throw new Error("Could not get preferences dictionary");
 
         if (aDict.has("RecentSearchStrings")) {
           let recentSearchStrings = aDict.get("RecentSearchStrings");
           if (recentSearchStrings && recentSearchStrings.length > 0) {
-            let formHistory = Cc["@mozilla.org/satchel/form-history;1"].
-                              getService(Ci.nsIFormHistory2);
-            for (let searchString of recentSearchStrings) {
-              formHistory.addEntry("searchbar-history", searchString);
-            }
+            let changes = [{op: "add",
+                            fieldname: "searchbar-history",
+                            value: searchString}
+                           for (searchString of recentSearchStrings)];
+            FormHistory.update(changes);
           }
         }
       }.bind(this), aCallback));
   }
 };
 
 #ifdef XP_MACOSX
 // On OS X, the cookie-accept policy preference is stored in a separate
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -713,17 +713,23 @@ PlacesTreeView.prototype = {
     NS_ASSERT(this._result, "Got a notification but have no result!");
     if (!this._tree || !this._result)
       return;
 
     // Bail out for hidden separators.
     if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
       return;
 
-    let oldRow = this._getRowForNode(aNode, true);
+    // Note that at this point the node has already been moved by the backend,
+    // so we must give hints to _getRowForNode to get the old row position.
+    let oldParentRow = aOldParent == this._rootNode ?
+                         undefined : this._getRowForNode(aOldParent, true);
+    let oldRow = this._getRowForNode(aNode, true, oldParentRow, aOldIndex);
+    if (oldRow < 0)
+      throw Cr.NS_ERROR_UNEXPECTED;
 
     // If this node is a container it could take up more than one row.
     let count = this._countVisibleRowsForNodeAtRow(oldRow);
 
     // Persist selection state.
     let nodesToReselect =
       this._getSelectedNodesInRange(oldRow, oldRow + count);
     if (nodesToReselect.length > 0)
--- a/browser/components/places/tests/chrome/head.js
+++ b/browser/components/places/tests/chrome/head.js
@@ -48,8 +48,22 @@ function addVisits(aPlaceInfo, aCallback
       handleResult: function () {},
       handleCompletion: function UP_handleCompletion() {
         if (aCallback)
           aCallback();
       }
     }
   );
 }
+
+/**
+ * Clears history invoking callback when done.
+ */
+function waitForClearHistory(aCallback) {
+  const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
+  Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(observer, TOPIC_EXPIRATION_FINISHED);
+    aCallback();
+  }, TOPIC_EXPIRATION_FINISHED, false);
+  Cc["@mozilla.org/browser/nav-history-service;1"]
+    .getService(Ci.nsINavHistoryService)
+    .QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
+}
--- a/browser/components/places/tests/chrome/test_bug549192.xul
+++ b/browser/components/places/tests/chrome/test_bug549192.xul
@@ -30,86 +30,86 @@
     <treecols>
       <treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
     </treecols>
     <treechildren flex="1"/>
   </tree>
 
   <script type="application/javascript"><![CDATA[
     /**
+     * Bug 874407
+     * Ensures that history views are updated properly after visits.
      * Bug 549192
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=549192
-     *
      * Ensures that history views are updated after deleting entries.
      */
 
     SimpleTest.waitForExplicitFinish();
 
     function runTest() {
       // The mochitest page is added to history.
       waitForClearHistory(continue_test);
     }
 
     function continue_test() {
       // Add some visits.
       let vtime = Date.now() * 1000;
       const ttype = PlacesUtils.history.TRANSITION_TYPED;
-
-      addVisits(
+      let places =
         [{ uri: Services.io.newURI("http://example.tld/", null, null),
-           visitDate: vtime, transition: ttype },
+           visitDate: ++vtime, transition: ttype },
          { uri: Services.io.newURI("http://example2.tld/", null, null),
-           visitDate: vtime++, transition: ttype },
+           visitDate: ++vtime, transition: ttype },
          { uri: Services.io.newURI("http://example3.tld/", null, null),
-           visitDate: vtime++, transition: ttype }],
-         function() {
-           // Make a history query.
-           let query = PlacesUtils.history.getNewQuery();
-           let opts = PlacesUtils.history.getNewQueryOptions();
-           let queryURI = PlacesUtils.history.queriesToQueryString([query], 1, opts);
+           visitDate: ++vtime, transition: ttype }];
+
+      addVisits(places, function() {
+        // Make a history query.
+        let query = PlacesUtils.history.getNewQuery();
+        let opts = PlacesUtils.history.getNewQueryOptions();
+        opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
+        let queryURI = PlacesUtils.history.queriesToQueryString([query], 1, opts);
 
-           // Setup the places tree contents.
-           var tree = document.getElementById("tree");
-           tree.place = queryURI;
+        // Setup the places tree contents.
+        var tree = document.getElementById("tree");
+        tree.place = queryURI;
+
+        // loop through the rows and check them.
+        let treeView = tree.view;
+        let selection = treeView.selection;
+        let rc = treeView.rowCount;
 
-           // loop through the rows and check formatting
-           let treeView = tree.view;
-           for (let i = 0; i < rc; i++) {
-             selection.select(rc);
-             let node = tree.selectedNode;
-             ok(true, "found " + node.title);
-           }
-           let rc = treeView.rowCount;
-           is(rc, 3, "Rows found.");
-           let selection = treeView.selection;
-           for (let i = 0; i < rc; i++) {
-             selection.select(0);
-             let node = tree.selectedNode;
-             tree.controller.remove("Removing page");
-             ok(treeView.treeIndexForNode(node) == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE,
-               node.uri + " removed.");
-             ok(treeView.rowCount == rc - i - 1, "Rows count decreased");
-           }
+        for (let i = 0; i < rc; i++) {
+          selection.select(i);
+          let node = tree.selectedNode;
+          is(node.uri, places[rc - i - 1].uri.spec,
+             "Found expected node at position " + i + ".");
+        }
+
+        is(rc, 3, "Found expected number of rows.");
 
-           // Cleanup.
-           waitForClearHistory(SimpleTest.finish);
-         });
-    }
+        // First check live-update of the view when adding visits.
+        places.forEach(place => place.visitDate = ++vtime);
+        addVisits(places, function() {
+          for (let i = 0; i < rc; i++) {
+            selection.select(i);
+            let node = tree.selectedNode;
+            is(node.uri, places[rc - i - 1].uri.spec,
+               "Found expected node at position " + i + ".");
+          }
 
-    /**
-     * Clears history invoking callback when done.
-     */
-    function waitForClearHistory(aCallback) {
-      const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
-      let observer = {
-        observe: function(aSubject, aTopic, aData) {
-          Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
-          aCallback();
-        }
-      };
-      Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
-      let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsINavHistoryService);
-      hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
-   }
+          // Now remove the pages and verify live-update again.
+          for (let i = 0; i < rc; i++) {
+            selection.select(0);
+            let node = tree.selectedNode;
+            tree.controller.remove("Removing page");
+            ok(treeView.treeIndexForNode(node) == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE,
+              node.uri + " removed.");
+            ok(treeView.rowCount == rc - i - 1, "Rows count decreased");
+          }
+
+          // Cleanup.
+          waitForClearHistory(SimpleTest.finish);
+        });
+      });
+    }
 
   ]]></script>
 </window>
--- a/browser/devtools/debugger/test/browser_dbg_globalactor-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_globalactor-01.js
@@ -22,36 +22,33 @@ function test()
          "testTabActor's actorPrefix should be used.");
       gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
         is(aResponse.pong, "pong", "Actor should respond to requests.");
         // Send another ping to see if the same actor is used.
         gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
           is(aResponse.pong, "pong", "Actor should respond to requests.");
 
           // Make sure that lazily-created actors are created only once.
-          let connections = Object.keys(DebuggerServer._connections);
-          info(connections.length + " connections are established.");
-          let connPrefix = connections[connections.length - 1];
-          ok(DebuggerServer._connections[connPrefix],
-             connPrefix + " is a valid connection.");
+          let conn = transport._serverConnection;
           // First we look for the pool of global actors.
-          let extraPools = DebuggerServer._connections[connPrefix]._extraPools;
+          let extraPools = conn._extraPools;
+
           let globalPool;
           for (let pool of extraPools) {
             if (Object.keys(pool._actors).some(function(elem) {
               // Tab actors are in the global pool.
-              let re = new RegExp(connPrefix + "tab", "g");
+              let re = new RegExp(conn._prefix + "tab", "g");
               return elem.match(re) !== null;
             })) {
               globalPool = pool;
               break;
             }
           }
           // Then we look if the global pool contains only one test actor.
-          let actorPrefix = connPrefix + "testone";
+          let actorPrefix = conn._prefix + "testone";
           let actors = Object.keys(globalPool._actors).join();
           info("Global actors: " + actors);
           isnot(actors.indexOf(actorPrefix), -1, "The test actor exists in the pool.");
           is(actors.indexOf(actorPrefix), actors.lastIndexOf(actorPrefix),
              "Only one actor exists in the pool.");
 
           finish_test();
         });
--- a/browser/metro/shell/testing/metrotestharness.cpp
+++ b/browser/metro/shell/testing/metrotestharness.cpp
@@ -223,16 +223,26 @@ static bool Launch()
     if (FAILED(hr)) {
       Fail(L"CoAllowSetForegroundWindow result %X", hr);
       return false;
     }
   }
 
   Log(L"Harness process id: %d", GetCurrentProcessId());
 
+  // If provided, validate the firefox path passed in.
+  int binLen = wcslen(kFirefoxExe);
+  if (sFirefoxPath.GetLength() && sFirefoxPath.Right(binLen) != kFirefoxExe) {
+    Log(L"firefoxpath is missing a valid bin name! Assuming '%s'.", kFirefoxExe);
+    if (sFirefoxPath.Right(1) != L"\\") {
+      sFirefoxPath += L"\\";
+    }
+    sFirefoxPath += kFirefoxExe;
+  }
+
   // Because we can't pass command line args, we store params in a
   // tests.ini file in dist/bin which the browser picks up on launch.
   CStringA testFilePath;
   if (sFirefoxPath.GetLength()) {
     // Use the firefoxpath passed to us by the test harness
     int index = sFirefoxPath.ReverseFind('\\');
     if (index == -1) {
       Fail(L"Bad firefoxpath path");
@@ -249,32 +259,48 @@ static bool Launch()
       return false;
     }
     char* slash = strrchr(path, '\\');
     if (!slash)
       return false;
     *slash = '\0'; // no trailing slash
     testFilePath = path;
     testFilePath += "\\";
+    sFirefoxPath = testFilePath;
+    sFirefoxPath += kFirefoxExe;
     testFilePath += kMetroTestFile;
   }
 
+  // Make sure the firefox bin exists
+  if (GetFileAttributesW(sFirefoxPath) == INVALID_FILE_ATTRIBUTES) {
+    Fail(L"Invalid bin path: '%s'", sFirefoxPath);
+    return false;
+  }
+
+  Log(L"Using bin path: '%s'", sFirefoxPath);
+
   Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath));
   HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE,
                                  0, NULL, CREATE_ALWAYS,
                                  FILE_ATTRIBUTE_NORMAL,
                                  NULL);
   if (hTestFile == INVALID_HANDLE_VALUE) {
     Fail(L"CreateFileA errorno=%d", GetLastError());
     return false;
   }
 
   DeleteTestFileHelper dtf(testFilePath);
 
-  CStringA asciiParams = sAppParams;
+  // nsAppRunner expects the first param to be the bin path, just like a
+  // normal startup. So prepend our bin path to our param string we write.
+  CStringA asciiParams = sFirefoxPath;
+  asciiParams += " ";
+  asciiParams += sAppParams;
+  asciiParams.Trim();
+  Log(L"Browser command line args: '%s'", CString(asciiParams));
   if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) {
     CloseHandle(hTestFile);
     Fail(L"WriteFile errorno=%d", GetLastError());
     return false;
   }
   FlushFileBuffers(hTestFile);
   CloseHandle(hTestFile);
 
@@ -345,17 +371,12 @@ int wmain(int argc, WCHAR* argv[])
       sFirefoxPath = param;
       continue;
     }
 
     sAppParams.Append(argv[idx]);
     sAppParams.Append(L" ");
   }
   sAppParams.Trim();
-  if (sFirefoxPath.GetLength()) {
-    Log(L"firefoxpath: '%s'", sFirefoxPath);
-  }
-  Log(L"args: '%s'", sAppParams);
   Launch();
-
   CoUninitialize();
   return 0;
 }
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ b/browser/modules/BrowserNewTabPreloader.jsm
@@ -57,17 +57,20 @@ this.BrowserNewTabPreloader = {
     HostFrame.destroy();
     Preferences.uninit();
     HiddenBrowsers.uninit();
   },
 
   newTab: function Preloader_newTab(aTab) {
     let win = aTab.ownerDocument.defaultView;
     if (win.gBrowser) {
-      let {boxObject: {width, height}} = win.gBrowser;
+      let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindowUtils);
+
+      let {width, height} = utils.getBoundsWithoutFlushing(win.gBrowser);
       let hiddenBrowser = HiddenBrowsers.get(width, height)
       if (hiddenBrowser) {
         return hiddenBrowser.swapWithNewTab(aTab);
       }
     }
 
     return false;
   }
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1555,20 +1555,24 @@ public:
    *
    * NOTE: If the site is optimized for mobile (via the doctype), this
    * will return viewport information that specifies default information.
    */
   static nsViewportInfo GetViewportInfo(nsIDocument* aDocument,
                                         uint32_t aDisplayWidth,
                                         uint32_t aDisplayHeight);
 
+#ifdef MOZ_WIDGET_ANDROID
   /**
    * The device-pixel-to-CSS-px ratio used to adjust meta viewport values.
+   * XXX Not to be used --- use nsIWidget::GetDefaultScale instead. Will be
+   * removed when bug 803207 is fixed.
    */
   static double GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget);
+#endif
 
   // Call EnterMicroTask when you're entering JS execution.
   // Usually the best way to do this is to use nsAutoMicroTask.
   static void EnterMicroTask() { ++sMicroTaskLevel; }
   static void LeaveMicroTask();
 
   static bool IsInMicroTask() { return sMicroTaskLevel != 0; }
   static uint32_t MicroTaskLevel() { return sMicroTaskLevel; }
--- a/content/base/public/nsIDOMDataChannel.idl
+++ b/content/base/public/nsIDOMDataChannel.idl
@@ -10,28 +10,29 @@
 #ifdef GetBinaryType
 // Windows apparently has a #define for GetBinaryType...
 #undef GetBinaryType
 #endif
 %}
 
 interface nsIVariant;
 
-[scriptable, builtinclass, uuid(4410f28d-c9eb-481d-a47e-e7ef49f382c8)]
+[scriptable, builtinclass, uuid(af7077ac-0f9f-44e1-815f-9b1a94618531)]
 interface nsIDOMDataChannel : nsIDOMEventTarget
 {
   readonly attribute DOMString label;
   readonly attribute DOMString protocol;
   readonly attribute boolean reliable;
   readonly attribute boolean ordered;
 
   readonly attribute DOMString readyState;
   readonly attribute unsigned long bufferedAmount;
 
-  readonly attribute unsigned short stream;
+  readonly attribute unsigned short id;
+  readonly attribute unsigned short stream; /* deprecated name for 'id' */
 
   [implicit_jscontext] attribute jsval onopen;
   [implicit_jscontext] attribute jsval onerror;
   [implicit_jscontext] attribute jsval onclose;
   [implicit_jscontext] attribute jsval onmessage;
 
   attribute DOMString binaryType;
 
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -338,35 +338,39 @@ EventSource::OnStartRequest(nsIRequest *
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool requestSucceeded;
   rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  uint32_t status;
-  rv = httpChannel->GetResponseStatus(&status);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (status == 204) {
-    mInterrupted = true;
-    DispatchFailConnection();
-    return NS_ERROR_ABORT;
-  }
-
   nsAutoCString contentType;
   rv = httpChannel->GetContentType(contentType);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!requestSucceeded || !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
+  nsresult status;
+  aRequest->GetStatus(&status);
+
+  if (NS_FAILED(status) || !requestSucceeded ||
+      !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
     DispatchFailConnection();
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  uint32_t httpStatus;
+  rv = httpChannel->GetResponseStatus(&httpStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (httpStatus != 200) {
+    mInterrupted = true;
+    DispatchFailConnection();
+    return NS_ERROR_ABORT;
+  }
+
   nsCOMPtr<nsIPrincipal> principal = mPrincipal;
   if (nsContentUtils::IsSystemPrincipal(principal)) {
     // Don't give this channel the system principal.
     principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   rv = httpChannel->SetOwner(principal);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4897,16 +4897,17 @@ static void ProcessViewportToken(nsIDocu
 nsViewportInfo
 nsContentUtils::GetViewportInfo(nsIDocument *aDocument,
                                 uint32_t aDisplayWidth,
                                 uint32_t aDisplayHeight)
 {
   return aDocument->GetViewportInfo(aDisplayWidth, aDisplayHeight);
 }
 
+#ifdef MOZ_WIDGET_ANDROID
 /* static */
 double
 nsContentUtils::GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget)
 {
   int32_t prefValue = Preferences::GetInt("browser.viewport.scaleRatio", 0);
   if (prefValue > 0) {
     return double(prefValue) / 100.0;
   }
@@ -4918,16 +4919,17 @@ nsContentUtils::GetDevicePixelsPerMetaVi
   }
   if (dpi < 300.0) {
     // Includes Nokia N900, and HDPI Android devices
     return 1.5;
   }
   // For very high-density displays like the iPhone 4, use an integer ratio.
   return floor(dpi / 150.0);
 }
+#endif
 
 /* static */
 nsresult
 nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
                                     const nsAString &viewportInfo) {
 
   /* We never fail. */
   nsresult rv = NS_OK;
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -122,16 +122,29 @@ nsDOMDataChannel::GetLabel(nsAString& aL
 NS_IMETHODIMP
 nsDOMDataChannel::GetProtocol(nsAString& aProtocol)
 {
   mDataChannel->GetProtocol(aProtocol);
   return NS_OK;
 }
 
 uint16_t
+nsDOMDataChannel::Id() const
+{
+  return mDataChannel->GetStream();
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetId(uint16_t *aId)
+{
+  *aId = Id();
+  return NS_OK;
+}
+
+uint16_t
 nsDOMDataChannel::Stream() const
 {
   return mDataChannel->GetStream();
 }
 
 NS_IMETHODIMP
 nsDOMDataChannel::GetStream(uint16_t *aStream)
 {
--- a/content/base/src/nsDOMDataChannel.h
+++ b/content/base/src/nsDOMDataChannel.h
@@ -67,17 +67,18 @@ public:
   }
   void Send(const nsAString& aData, mozilla::ErrorResult& aRv);
   void Send(nsIDOMBlob* aData, mozilla::ErrorResult& aRv);
   void Send(mozilla::dom::ArrayBuffer& aData, mozilla::ErrorResult& aRv);
   void Send(mozilla::dom::ArrayBufferView& aData, mozilla::ErrorResult& aRv);
 
   // Uses XPIDL GetProtocol.
   bool Ordered() const;
-  uint16_t Stream() const;
+  uint16_t Id() const;
+  uint16_t Stream() const; // deprecated
 
   nsresult
   DoOnMessageAvailable(const nsACString& aMessage, bool aBinary);
 
   virtual nsresult
   OnMessageAvailable(nsISupports* aContext, const nsACString& aMessage) MOZ_OVERRIDE;
 
   virtual nsresult
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6887,17 +6887,22 @@ nsDocument::GetViewportInfo(uint32_t aDi
       if (aDisplayWidth > 0 && aDisplayHeight > 0) {
         height = uint32_t((width * aDisplayHeight) / aDisplayWidth);
       } else {
         height = width;
       }
     }
     // Now convert the scale into device pixels per CSS pixel.
     nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
+#ifdef MOZ_WIDGET_ANDROID
+    // Temporarily use special Android code until bug 803207 is fixed
     double pixelRatio = widget ? nsContentUtils::GetDevicePixelsPerMetaViewportPixel(widget) : 1.0;
+#else
+    double pixelRatio = widget ? widget->GetDefaultScale() : 1.0;
+#endif
     float scaleFloat = mScaleFloat * pixelRatio;
     float scaleMinFloat= mScaleMinFloat * pixelRatio;
     float scaleMaxFloat = mScaleMaxFloat * pixelRatio;
 
     if (mAutoSize) {
       // aDisplayWidth and aDisplayHeight are in device pixels; convert them to
       // CSS pixels for the viewport size.
       width = aDisplayWidth / pixelRatio;
@@ -9398,17 +9403,21 @@ nsIDocument::CaretPositionFromPoint(floa
                      isText)) {
       // If the anonymous content node has a child, then we need to make sure
       // that we get the appropriate child, as otherwise the offset may not be
       // correct when we construct a range for it.
       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
       if (firstChild) {
         anonNode = firstChild;
       }
-      offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
+
+      if (textArea) {
+        offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
+      }
+
       node = nonanon;
     } else {
       node = nullptr;
       offset = 0;
     }
   }
 
   nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -757,16 +757,17 @@ GK_ATOM(onpageshow, "onpageshow")
 GK_ATOM(onpaint, "onpaint")
 GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
 GK_ATOM(onpopupshowing, "onpopupshowing")
 GK_ATOM(onpopupshown, "onpopupshown")
 GK_ATOM(onreadystatechange, "onreadystatechange")
 GK_ATOM(onreceived, "onreceived")
+GK_ATOM(onretrieving, "onretrieving")
 GK_ATOM(onRequest, "onRequest")
 GK_ATOM(onreset, "onreset")
 GK_ATOM(onresuming, "onresuming")
 GK_ATOM(onMozBeforeResize, "onMozBeforeResize")
 GK_ATOM(onresize, "onresize")
 GK_ATOM(onscroll, "onscroll")
 GK_ATOM(onselect, "onselect")
 GK_ATOM(onsending, "onsending")
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -231,16 +231,17 @@ MOCHITEST_FILES_A = \
 		test_domparsing.html \
 		test_meta_viewport0.html \
 		test_meta_viewport1.html \
 		test_meta_viewport2.html \
 		test_meta_viewport3.html \
 		test_meta_viewport4.html \
 		test_meta_viewport5.html \
 		test_meta_viewport6.html \
+			viewport_helpers.js \
 		test_mutationobservers.html \
 		mutationobserver_dialog.html \
 		test_bug744830.html \
 		file_bug782342.txt \
 		test_bug782342.html \
 		test_bug282547.html \
 		bug282547.sjs \
 		test_domparser_null_char.html \
--- a/content/base/test/test_caretPositionFromPoint.html
+++ b/content/base/test/test_caretPositionFromPoint.html
@@ -14,42 +14,37 @@
       font-family: Ahem;
       src: url("Ahem.ttf");
     }
 
     #a {
       font-family: Ahem;
       padding: 10px;
       border: 8px solid black;
-      width: 600px;
+      width: 450px;
     }
 
-    #dp {
-      position: absolute;
-      width: 2px;
-      height: 2px;
-      left: 0px;
-      top: 0px;
-      background-color: orange;
+    #test5 {
+      height: 100px;
     }
   </style>
 <script>
-  function displayPoint(aX, aY) {
-    document.getElementById('dp').style['left'] = aX + "px";
-    document.getElementById('dp').style['top'] = aY + "px";
-  }
-
   function convertEmToPx(aEm) {
     // Assumes our base font size is 16px = 12pt = 1.0em.
     var pxPerEm = 16.0 / 1.0;
     return pxPerEm * aEm;
   }
 
   function checkOffsetsFromPoint(aX, aY, aExpected, aElementName='no-name') {
     var cp = document.caretPositionFromPoint(aX, aY);
+    if (!cp) {
+      ok(false, 'caretPositionFromPoint returned null for point: (' + aX + ', ' + aY + ')');
+      return;
+    }
+
     ok(aExpected == cp.offset, 'expected offset at (' + aX + ', ' + aY + ') [' + aElementName + ']: ' + aExpected + ', got: ' + cp.offset);
   }
 
   function doTesting() {
     var test1Element = document.getElementById("test1");
     var test1Rect = test1Element.getBoundingClientRect();
 
     // Check the first and last characters of the basic div.
@@ -82,24 +77,32 @@
     ok(!nullCp1, "caret position with negative x should be null");
     var nullCp2 = document.caretPositionFromPoint(0, -10);
     ok(!nullCp2, "caret position with negative y should be null");
     var nullCp3 = document.caretPositionFromPoint(9000, 0);
     ok(!nullCp3, "caret position with x > viewport width should be null");
     var nullCp4 = document.caretPositionFromPoint(0, 9000);
     ok(!nullCp4, "caret position with x > viewport height should be null");
 
-    // Check the first and last characters of the marquee.
+    // Check a point within the bottom whitespace of the input.
+    var test5Element = document.getElementById('test5');
+    var test5Rect = test5Element.getBoundingClientRect();
+    var test5x = test5Rect.left + 5;
+    var test5y = test5Rect.bottom - 10;
+
+    todo(false, "test5Rect: (" + test5Rect.top + ", " + test5Rect.left + ", " + test5Rect.width + ", " + test5Rect.height + ")");
+    checkOffsetsFromPoint(test5x, test5y, 0, 'test5');
+
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
 </script>
 </head>
 <body onload="doTesting();">
-<div id="dp"></div>
 <div id="a" contenteditable><span id="test1">abc, abc, abc</span><br>
 <span id="test2" style="color: blue;">abc, abc, abc</span><br>
 <textarea id="test3">abc</textarea><input id="test4" value="abcdef"><br><br>
 <marquee>marquee</marquee>
 </div>
+<input id="test5" value="The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly down, so suddenly that Alice had not a moment to think about stopping herself before she found herself falling down a very deep well. Either the well was very deep, or she fell very slowly, for she had plenty of time as she went down to look about her and to wonder what was going to happen next. First, she tried to look down and make out what she was coming to, but it was too dark to see anything; then she looked at the sides of the well, and noticed that they were filled with cupboards and book-shelves; here and there she saw maps and pictures hung upon pegs. She took down a jar from one of the shelves as she passed; it was labelled `ORANGE MARMALADE', but to her great disappointment it was empty: she did not like to drop the jar for fear of killing somebody, so managed to put it into one of the cupboards as she fell past it." type="text">
 </body>
 </html>
--- a/content/base/test/test_meta_viewport0.html
+++ b/content/base/test/test_meta_viewport0.html
@@ -1,27 +1,28 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>No &lt;meta name="viewport"&gt; tag</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 0,       "initial scale is unspecified");
           is(info.minZoom,     0,       "minumum scale defaults to the absolute minumum");
           is(info.maxZoom,     10,      "maximum scale defaults to the absolute maximum");
           is(info.width,       980,     "width is the default width");
           is(info.height,      588,     "height is proportional to displayHeight");
           is(info.autoSize,    false,   "autoSize is disabled by default");
@@ -31,17 +32,17 @@
           is(info.width,       980,     "width is still the default width");
           is(info.height,      1200,    "height is proportional to the new displayHeight");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.width,       980,     "width is still the default width");
           is(info.height,      588,     "height is still proportional to displayHeight");
 
           nextTest();
         });
     });
--- a/content/base/test/test_meta_viewport1.html
+++ b/content/base/test/test_meta_viewport1.html
@@ -1,28 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width, initial-scale=1">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width, initial-scale=1</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 1,    "initial zoom is 100%");
           is(info.width,       800,  "width is the same as the displayWidth");
           is(info.height,      480,  "height is the same as the displayHeight");
           is(info.autoSize,    true, "width=device-width enables autoSize");
           is(info.allowZoom,   true, "zooming is enabled by default");
 
@@ -30,17 +31,17 @@
           is(info.width,       900,  "changing the displayWidth changes the width");
           is(info.height,      600,  "changing the displayHeight changes the height");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           let info = getViewportInfo(900, 600);
           is(info.defaultZoom, 1.5,  "initial zoom is 150%");
           is(info.width,       600,  "width equals displayWidth/1.5");
           is(info.height,      400,  "height equals displayHeight/1.5");
 
           nextTest();
         });
--- a/content/base/test/test_meta_viewport2.html
+++ b/content/base/test/test_meta_viewport2.html
@@ -1,28 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 1,    "initial zoom is 100%");
           is(info.width,       800,  "width is the same as the displayWidth");
           is(info.height,      480,  "height is the same as the displayHeight");
           is(info.autoSize,    true, "width=device-width enables autoSize");
           is(info.allowZoom,   true, "zooming is enabled by default");
 
@@ -30,17 +31,17 @@
           is(info.width,       900,  "changing the displayWidth changes the width");
           is(info.height,      600,  "changing the displayHeight changes the height");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           let info = getViewportInfo(900, 600);
           is(info.defaultZoom, 1.5,  "initial zoom is 150%");
           is(info.width,       600,  "width equals displayWidth/1.5");
           is(info.height,      400,  "height equals displayHeight/1.5");
 
           nextTest();
         });
--- a/content/base/test/test_meta_viewport3.html
+++ b/content/base/test/test_meta_viewport3.html
@@ -1,28 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=320">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=320</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 2.5,   "initial zoom fits the displayWidth");
           is(info.width,       320,   "width is set explicitly");
           is(info.height,      223,   "height is at the absolute minimum");
           is(info.autoSize,    false, "width=device-width enables autoSize");
           is(info.allowZoom,   true,  "zooming is enabled by default");
 
@@ -31,17 +32,17 @@
           is(info.width,       320,   "explicit width is unchanged");
           is(info.height,      533,   "height changes proportional to displayHeight");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           // With an explicit width in CSS px, the scaleRatio has no effect.
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 2.5,   "initial zoom still fits the displayWidth");
           is(info.width,       320,   "width is still set explicitly");
           is(info.height,      223,   "height is still minimum height");
 
           nextTest();
--- a/content/base/test/test_meta_viewport4.html
+++ b/content/base/test/test_meta_viewport4.html
@@ -1,28 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale=1.0, user-scalable=no</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 1,     "initial zoom is set explicitly");
           is(info.width,       800,   "width fits the initial zoom level");
           is(info.height,      480,   "height fits the initial zoom level");
           is(info.autoSize,    true,  "initial-scale=1 enables autoSize");
           is(info.allowZoom,   false, "zooming is explicitly disabled");
 
@@ -31,17 +32,17 @@
           is(info.width,       480,   "width changes to match the displayWidth");
           is(info.height,      800,   "height changes to match the displayHeight");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.defaultZoom, 1.5,   "initial zoom is adjusted for device pixel ratio");
           is(info.width,       533,   "width fits the initial zoom");
           is(info.height,      320,   "height fits the initial zoom");
 
           nextTest();
         });
--- a/content/base/test/test_meta_viewport5.html
+++ b/content/base/test/test_meta_viewport5.html
@@ -1,16 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="user-scalable=NO">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>user-scalable=NO</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
--- a/content/base/test/test_meta_viewport6.html
+++ b/content/base/test/test_meta_viewport6.html
@@ -1,28 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=2000, minimum-scale=0.75">
+  <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=2000, minimum-scale=0.75</p>
   <script type="application/javascript;version=1.7">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
     let tests = [];
 
     tests.push(function test1() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.0),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.minZoom,     0.75,    "minumum scale is set explicitly");
           is(info.defaultZoom, 0.75,    "initial scale is bounded by the minimum scale");
           is(info.maxZoom,     10,      "maximum scale defaults to the absolute maximum");
           is(info.width,       2000,    "width is set explicitly");
           is(info.height,      1200,    "height is proportional to displayHeight");
           is(info.autoSize,    false,   "autoSize is disabled by default");
@@ -34,17 +35,17 @@
           is(info.width,       2000,    "width is set explicitly");
           is(info.height,      1000,    "height is proportional to the new displayHeight");
 
           nextTest();
         });
     });
 
     tests.push(function test2() {
-      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+      SpecialPowers.pushPrefEnv(scaleRatio(1.5),
         function() {
           let info = getViewportInfo(800, 480);
           is(info.minZoom,     1.125,   "minumum scale is converted to device pixel scale");
           is(info.defaultZoom, 1.125,   "initial scale is bounded by the minimum scale");
           is(info.maxZoom,     15,      "maximum scale defaults to the absolute maximum");
           is(info.width,       2000,    "width is still set explicitly");
           is(info.height,      1200,    "height is still proportional to displayHeight");
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/viewport_helpers.js
@@ -0,0 +1,6 @@
+function scaleRatio(scale) {
+  if (navigator.appVersion.indexOf("Android") >= 0) {
+    return {"set": [["browser.viewport.scaleRatio", 100*scale]]};
+  }
+  return {"set": [["layout.css.devPixelsPerPx", "" + scale]]};
+}
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -3207,17 +3207,17 @@ CanvasRenderingContext2D::DrawWindow(nsI
   if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
   }
 
   // gfxContext-over-Azure may modify the DrawTarget's transform, so
   // save and restore it
   Matrix matrix = mTarget->GetTransform();
   nsRefPtr<gfxContext> thebes;
-  if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+  if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget)) {
     thebes = new gfxContext(mTarget);
   } else {
     nsRefPtr<gfxASurface> drawSurf;
     GetThebesSurface(getter_AddRefs(drawSurf));
     thebes = new gfxContext(drawSurf);
   }
   thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
                               matrix._22, matrix._31, matrix._32));
--- a/content/canvas/test/webgl/failing_tests_linux_mesa.txt
+++ b/content/canvas/test/webgl/failing_tests_linux_mesa.txt
@@ -1,5 +1,3 @@
-conformance/textures/texture-mips.html
 conformance/textures/texture-size-cube-maps.html
 conformance/extensions/oes-texture-float.html
 conformance/glsl/functions/glsl-function-sin.html
-
--- a/content/canvas/test/webgl/skipped_tests_linux_mesa.txt
+++ b/content/canvas/test/webgl/skipped_tests_linux_mesa.txt
@@ -1,3 +1,3 @@
 conformance/misc/type-conversion-test.html
 conformance/reading/read-pixels-test.html
-
+conformance/textures/texture-mips.html
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -3776,20 +3776,20 @@ nsEventStateManager::SetCursor(int32_t a
     c = eCursor_grab;
     break;
   case NS_STYLE_CURSOR_GRABBING:
     c = eCursor_grabbing;
     break;
   case NS_STYLE_CURSOR_SPINNING:
     c = eCursor_spinning;
     break;
-  case NS_STYLE_CURSOR_MOZ_ZOOM_IN:
+  case NS_STYLE_CURSOR_ZOOM_IN:
     c = eCursor_zoom_in;
     break;
-  case NS_STYLE_CURSOR_MOZ_ZOOM_OUT:
+  case NS_STYLE_CURSOR_ZOOM_OUT:
     c = eCursor_zoom_out;
     break;
   case NS_STYLE_CURSOR_NOT_ALLOWED:
     c = eCursor_not_allowed;
     break;
   case NS_STYLE_CURSOR_COL_RESIZE:
     c = eCursor_col_resize;
     break;
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -497,18 +497,16 @@ public:
   uint32_t GetMozSampleRate(ErrorResult& aRv) const;
 
   uint32_t GetMozFrameBufferLength(ErrorResult& aRv) const;
 
   void SetMozFrameBufferLength(uint32_t aValue, ErrorResult& aRv);
 
   JSObject* MozGetMetadata(JSContext* aCx, ErrorResult& aRv);
 
-  void MozLoadFrom(HTMLMediaElement& aOther, ErrorResult& aRv);
-
   double MozFragmentEnd();
 
   // XPCOM GetMozAudioChannelType() is OK
 
   void SetMozAudioChannelType(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::mozaudiochannel, aValue, aRv);
   }
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -2756,20 +2756,40 @@ HTMLInputElement::ShouldPreventDOMActiva
   }
 
   return target->GetParent() == this &&
          target->IsRootOfNativeAnonymousSubtree() &&
          target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                              nsGkAtoms::button, eCaseMatters);
 }
 
+void
+HTMLInputElement::MaybeFireAsyncClickHandler(nsEventChainPostVisitor& aVisitor)
+{
+  // Open a file picker when we receive a click on a <input type='file'>, or
+  // open a color picker when we receive a click on a <input type='color'>.
+  // A click is handled in the following cases:
+  // - preventDefault() has not been called (or something similar);
+  // - it's the left mouse button.
+  // We do not prevent non-trusted click because authors can already use
+  // .click(). However, the file picker will follow the rules of popup-blocking.
+  if ((mType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_COLOR) &&
+      NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) &&
+      !aVisitor.mEvent->mFlags.mDefaultPrevented) {
+    FireAsyncClickHandler();
+  }
+}
+
 nsresult
 HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (!aVisitor.mPresContext) {
+    // Hack alert! In order to open file picker even in case the element isn't
+    // in document, fire click handler even without PresContext.
+    MaybeFireAsyncClickHandler(aVisitor);
     return NS_OK;
   }
 
   if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
       aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     if (aVisitor.mEvent->message == NS_FOCUS_CONTENT &&
         MayFireChangeOnBlur()) {
       GetValueInternal(mFocusedValue);
@@ -3163,28 +3183,17 @@ HTMLInputElement::PostHandleEvent(nsEven
       mForm->FlushPendingSubmission();
     }
   } // if
 
   if (NS_SUCCEEDED(rv) && mType == NS_FORM_INPUT_RANGE) {
     PostHandleEventForRangeThumb(aVisitor);
   }
 
-  // Open a file picker when we receive a click on a <input type='file'>, or
-  // open a color picker when we receive a click on a <input type='color'>.
-  // A click is handled in the following cases:
-  // - preventDefault() has not been called (or something similar);
-  // - it's the left mouse button.
-  // We do not prevent non-trusted click because authors can already use
-  // .click(). However, the file picker will follow the rules of popup-blocking.
-  if ((mType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_COLOR) &&
-      NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) &&
-      !aVisitor.mEvent->mFlags.mDefaultPrevented) {
-    return FireAsyncClickHandler();
-  }
+  MaybeFireAsyncClickHandler(aVisitor);
 
   return rv;
 }
 
 void
 HTMLInputElement::PostHandleEventForRangeThumb(nsEventChainPostVisitor& aVisitor)
 {
   MOZ_ASSERT(mType == NS_FORM_INPUT_RANGE);
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -218,16 +218,17 @@ public:
    * button in the group.
    *
    * @return the selected button (or null).
    */
   already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
+  void MaybeFireAsyncClickHandler(nsEventChainPostVisitor& aVisitor);
   NS_IMETHOD FireAsyncClickHandler();
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLInputElement,
                                            nsGenericHTMLFormElement)
 
   static UploadLastDir* gUploadLastDir;
   // create and destroy the static UploadLastDir object for remembering
   // which directory was last used on a site-by-site basis
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1205,62 +1205,16 @@ nsresult HTMLMediaElement::LoadWithChann
   }
 
   SetPlaybackRate(mDefaultPlaybackRate);
   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
-void
-HTMLMediaElement::MozLoadFrom(HTMLMediaElement& aOther, ErrorResult& aRv)
-{
-  // Make sure we don't reenter during synchronous abort events.
-  if (mIsRunningLoadMethod) {
-    return;
-  }
-
-  mIsRunningLoadMethod = true;
-  AbortExistingLoads();
-  mIsRunningLoadMethod = false;
-
-  if (!aOther.mDecoder) {
-    return;
-  }
-
-  ChangeDelayLoadStatus(true);
-
-  mLoadingSrc = aOther.mLoadingSrc;
-  aRv = InitializeDecoderAsClone(aOther.mDecoder);
-  if (aRv.Failed()) {
-    ChangeDelayLoadStatus(false);
-    return;
-  }
-
-  SetPlaybackRate(mDefaultPlaybackRate);
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
-}
-
-NS_IMETHODIMP HTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
-{
-  NS_ENSURE_ARG_POINTER(aOther);
-
-  nsCOMPtr<nsIContent> content = do_QueryInterface(aOther);
-  HTMLMediaElement* other = static_cast<HTMLMediaElement*>(content.get());
-
-  if (!other) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ErrorResult rv;
-  MozLoadFrom(*other, rv);
-
-  return rv.ErrorCode();
-}
-
 /* readonly attribute unsigned short readyState; */
 NS_IMETHODIMP HTMLMediaElement::GetReadyState(uint16_t* aReadyState)
 {
   *aReadyState = ReadyState();
 
   return NS_OK;
 }
 
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -16,16 +16,23 @@
 #include "nsIWebProgressListener.h"
 #include "nsIRadioGroupContainer.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsThreadUtils.h"
 #include "nsInterfaceHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsAsyncDOMEvent.h"
 
+// Including 'windows.h' will #define GetClassInfo to something else.
+#ifdef XP_WIN
+#ifdef GetClassInfo
+#undef GetClassInfo
+#endif
+#endif
+
 class nsFormControlList;
 class nsIMutableArray;
 class nsIURI;
 
 class nsHTMLFormElement : public nsGenericHTMLElement,
                           public nsIDOMHTMLFormElement,
                           public nsIWebProgressListener,
                           public nsIForm,
@@ -214,18 +221,17 @@ public:
    *
    * @return Whether the form is valid.
    *
    * @note Do not call this method if novalidate/formnovalidate is used.
    * @note This method might disappear with bug 592124, hopefuly.
    */
   bool CheckValidFormSubmission();
 
-  // XXXdholbert This should be MOZ_OVERRIDE, or maybe dropped (see bug 877510)
-  virtual nsXPCClassInfo* GetClassInfo();
+  virtual nsXPCClassInfo* GetClassInfo() MOZ_OVERRIDE;
 
   virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
 
   /**
    * Walk over the form elements and call SubmitNamesValues() on them to get
    * their data pumped into the FormSubmitter.
    *
    * @param aFormSubmission the form submission object
--- a/content/html/content/test/forms/test_input_file_picker.html
+++ b/content/html/content/test/forms/test_input_file_picker.html
@@ -188,23 +188,36 @@ function runTests() {
       is(filters.length, testData[currentTest][1],
          "appendFilters not called as often as expected (" + testName + ")");
       is(filters[0], testData[currentTest][2],
          "Correct filters should have been added (" + testName + ")");
       is(filterIndex, testData[currentTest][3],
          "File picker should show the correct filter index (" + testName + ")");
 
       if (++currentTest == testData.length) {
-        MockFilePicker.cleanup();
-        SimpleTest.finish();
+        setTimeout(testDisconnectedElement, 0);
       } else {
         launchNextTest();
       }
     });
   };
 
   launchNextTest();
 }
 
+function testDisconnectedElement() {
+  MockFilePicker.shown = false;
+  MockFilePicker.showCallback = function(filepicker) {
+    ok(MockFilePicker.shown, "FilePicker should be open!");
+    MockFilePicker.shown = false;
+    MockFilePicker.cleanup();
+    SimpleTest.finish();
+  }
+  var f = document.createElement("input");
+  f.setAttribute("type", "file");
+  f.click();
+  ok(!MockFilePicker.shown, "FilePicker should open asynchronously!");
+}
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_audio_wakelock.html
+++ b/content/html/content/test/test_audio_wakelock.html
@@ -28,18 +28,18 @@ function testAudioPlayPause() {
 
   var audio = document.createElement('audio');
   audio.src = "wakelock.ogg";
   content.appendChild(audio);
 
   var startDate;
   audio.addEventListener('progress', function() {
     lockState = false;
+    startDate = new Date();
     audio.pause();
-    startDate = new Date();
   });
 
   navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
     is(topic, "cpu", "Audio element locked the target == cpu");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Audio element locked the cpu - no paused");
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -221,17 +221,17 @@ nsMathMLElement::IsAttributeMapped(const
     sCommonPresStyles,
     sDirStyles
   };
 
   // We don't support mglyph (yet).
   nsIAtom* tag = Tag();
   if (tag == nsGkAtoms::ms_ || tag == nsGkAtoms::mi_ ||
       tag == nsGkAtoms::mn_ || tag == nsGkAtoms::mo_ ||
-      tag == nsGkAtoms::mtext_)
+      tag == nsGkAtoms::mtext_ || tag == nsGkAtoms::mspace_)
     return FindAttributeDependence(aAttribute, tokenMap);
   if (tag == nsGkAtoms::mstyle_ ||
       tag == nsGkAtoms::math)
     return FindAttributeDependence(aAttribute, mstyleMap);
 
   if (tag == nsGkAtoms::mtable_)
     return FindAttributeDependence(aAttribute, mtableMap);
 
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -3,17 +3,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(GStreamerReader_h_)
 #define GStreamerReader_h_
 
 #include <gst/gst.h>
 #include <gst/app/gstappsrc.h>
 #include <gst/app/gstappsink.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreserved-user-defined-literal"
 #include <gst/video/video.h>
+#pragma GCC diagnostic pop
 #include <map>
 #include "MediaDecoderReader.h"
 
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -93,16 +93,17 @@ status_t MediaStreamSource::initCheck() 
   return OK;
 }
 
 ssize_t MediaStreamSource::readAt(off64_t offset, void *data, size_t size)
 {
   char *ptr = static_cast<char *>(data);
   size_t todo = size;
   while (todo > 0) {
+    Mutex::Autolock autoLock(mLock);
     uint32_t bytesRead;
     if ((offset != mResource->Tell() &&
          NS_FAILED(mResource->Seek(nsISeekableStream::NS_SEEK_SET, offset))) ||
         NS_FAILED(mResource->Read(ptr, todo, &bytesRead))) {
       return ERROR_IO;
     }
 
     if (bytesRead == 0) {
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -37,16 +37,17 @@ class VideoGraphicBuffer : public Graphi
 
 namespace android {
 
 // MediaStreamSource is a DataSource that reads from a MPAPI media stream.
 class MediaStreamSource : public DataSource {
   typedef mozilla::MediaResource MediaResource;
   typedef mozilla::AbstractMediaDecoder AbstractMediaDecoder;
 
+  Mutex mLock;
   nsRefPtr<MediaResource> mResource;
   AbstractMediaDecoder *mDecoder;
 public:
   MediaStreamSource(MediaResource* aResource,
                     AbstractMediaDecoder *aDecoder);
 
   virtual status_t initCheck() const;
   virtual ssize_t readAt(off64_t offset, void *data, size_t size);
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -86,17 +86,16 @@ MOCHITEST_FILES = \
 		test_info_leak.html \
 		test_invalid_reject.html \
 		test_load.html \
 		test_load_candidates.html \
 		test_load_same_resource.html \
 		test_load_source.html \
 		test_loop.html \
 		test_metadata.html \
-		test_mozLoadFrom.html \
 		test_no_load_event.html \
 		test_networkState.html \
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
 		test_play_events.html \
 		test_play_events_2.html \
 		$(filter disabled-temporarily--bug-751539, test_played.html) \
deleted file mode 100644
--- a/content/media/test/test_mozLoadFrom.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test mozLoadFrom API</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="text/javascript" src="manifest.js"></script>
-</head>
-<body>
-
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var manager = new MediaTestManager;
-
-function cloneLoaded(event) {
-  ok(true, "Clone loaded OK");
-  var e = event.target;
-
-  if (e._expectedDuration) {
-    ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
-       "Clone " + e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
-  }
-
-  manager.finished(e.token);
-}
-
-function tryClone(event) {
-  var e = event.target;
-  var clone = e.cloneNode(false);
-  clone.token = e.token;
-  
-  if (e._expectedDuration) {
-    ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
-       e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
-    clone._expectedDuration = e._expectedDuration;
-  }
-
-  clone.mozLoadFrom(e);
-  is(clone.currentSrc, e.currentSrc, "Clone source OK");
-
-  clone.addEventListener("loadeddata", cloneLoaded, false);
-}
-
-function initTest(test, token) {
-  var elemType = /^audio/.test(test.type) ? "audio" : "video";
-  var e = document.createElement(elemType);
-  if (e.canPlayType(test.type)) {
-    e.src = test.name;
-    if (test.duration) {
-      e._expectedDuration = test.duration;
-    }
-    e.addEventListener("loadeddata", tryClone, false);
-    e.load();
-    e.token = token;
-    manager.started(token);
-  }
-}
-
-manager.runTests(gCloneTests, initTest);
-
-</script>
-</pre>
-</body>
-</html>
--- a/content/media/webaudio/BiquadFilterNode.cpp
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -58,16 +58,19 @@ void SetParamsOnBiquad(WebCore::Biquad& 
     aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain);
     break;
   case BiquadFilterType::Notch:
     aBiquad.setNotchParams(normalizedFrequency, aQ);
     break;
   case BiquadFilterType::Allpass:
     aBiquad.setAllpassParams(normalizedFrequency, aQ);
     break;
+  default:
+    NS_NOTREACHED("We should never see the alternate names here");
+    break;
   }
 }
 
 class BiquadFilterNodeEngine : public AudioNodeEngine
 {
 public:
   BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
@@ -193,16 +196,31 @@ JSObject*
 BiquadFilterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return BiquadFilterNodeBinding::Wrap(aCx, aScope, this);
 }
 
 void
 BiquadFilterNode::SetType(BiquadFilterType aType)
 {
+  // Handle the alternate enum values
+  switch (aType) {
+  case BiquadFilterType::_0: aType = BiquadFilterType::Lowpass; break;
+  case BiquadFilterType::_1: aType = BiquadFilterType::Highpass; break;
+  case BiquadFilterType::_2: aType = BiquadFilterType::Bandpass; break;
+  case BiquadFilterType::_3: aType = BiquadFilterType::Lowshelf; break;
+  case BiquadFilterType::_4: aType = BiquadFilterType::Highshelf; break;
+  case BiquadFilterType::_5: aType = BiquadFilterType::Peaking; break;
+  case BiquadFilterType::_6: aType = BiquadFilterType::Notch; break;
+  case BiquadFilterType::_7: aType = BiquadFilterType::Allpass; break;
+  default:
+    // Shut up the compiler warning
+    break;
+  }
+
   mType = aType;
   SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE,
                              static_cast<int32_t>(aType));
 }
 
 void
 BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
                                        Float32Array& aMagResponse,
--- a/content/media/webaudio/PannerNode.cpp
+++ b/content/media/webaudio/PannerNode.cpp
@@ -63,30 +63,36 @@ public:
       mPanningModel = PanningModelType(aParam);
       switch (mPanningModel) {
         case PanningModelType::Equalpower:
           mPanningModelFunction = &PannerNodeEngine::EqualPowerPanningFunction;
           break;
         case PanningModelType::HRTF:
           mPanningModelFunction = &PannerNodeEngine::HRTFPanningFunction;
           break;
+        default:
+          NS_NOTREACHED("We should never see the alternate names here");
+          break;
       }
       break;
     case PannerNode::DISTANCE_MODEL:
       mDistanceModel = DistanceModelType(aParam);
       switch (mDistanceModel) {
         case DistanceModelType::Inverse:
           mDistanceModelFunction = &PannerNodeEngine::InverseGainFunction;
           break;
         case DistanceModelType::Linear:
           mDistanceModelFunction = &PannerNodeEngine::LinearGainFunction;
           break;
         case DistanceModelType::Exponential:
           mDistanceModelFunction = &PannerNodeEngine::ExponentialGainFunction;
           break;
+        default:
+          NS_NOTREACHED("We should never see the alternate names here");
+          break;
       }
       break;
     default:
       NS_ERROR("Bad PannerNodeEngine Int32Parameter");
     }
   }
   virtual void SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aParam) MOZ_OVERRIDE
   {
--- a/content/media/webaudio/PannerNode.h
+++ b/content/media/webaudio/PannerNode.h
@@ -37,26 +37,45 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode)
 
   PanningModelType PanningModel() const
   {
     return mPanningModel;
   }
   void SetPanningModel(PanningModelType aPanningModel)
   {
+    // Handle the alternate enum values
+    switch (aPanningModel) {
+    case PanningModelType::_0: aPanningModel = PanningModelType::Equalpower; break;
+    case PanningModelType::_1: aPanningModel = PanningModelType::HRTF; break;
+    default:
+      // Shut up the compiler warning
+      break;
+    }
+
     mPanningModel = aPanningModel;
     SendInt32ParameterToStream(PANNING_MODEL, int32_t(mPanningModel));
   }
 
   DistanceModelType DistanceModel() const
   {
     return mDistanceModel;
   }
   void SetDistanceModel(DistanceModelType aDistanceModel)
   {
+    // Handle the alternate enum values
+    switch (aDistanceModel) {
+    case DistanceModelType::_0: aDistanceModel = DistanceModelType::Linear; break;
+    case DistanceModelType::_1: aDistanceModel = DistanceModelType::Inverse; break;
+    case DistanceModelType::_2: aDistanceModel = DistanceModelType::Exponential; break;
+    default:
+      // Shut up the compiler warning
+      break;
+    }
+
     mDistanceModel = aDistanceModel;
     SendInt32ParameterToStream(DISTANCE_MODEL, int32_t(mDistanceModel));
   }
 
   void SetPosition(double aX, double aY, double aZ)
   {
     if (WebAudioUtils::FuzzyEqual(mPosition.x, aX) &&
         WebAudioUtils::FuzzyEqual(mPosition.y, aY) &&
--- a/content/media/webaudio/test/test_biquadFilterNode.html
+++ b/content/media/webaudio/test/test_biquadFilterNode.html
@@ -52,16 +52,18 @@ addLoadEvent(function() {
     "bandpass",
     "lowshelf",
     "highshelf",
     "peaking",
     "notch",
     "allpass",
   ];
   for (var i = 0; i < types.length; ++i) {
+    filter.type = filter[types[i].toUpperCase()];
+    is(filter.type, types[i], "Correct alternname type enum value");
     filter.type = types[i];
   }
 
   source.start(0);
   SimpleTest.executeSoon(function() {
     source.stop(0);
     source.disconnect();
     filter.disconnect();
--- a/content/media/webaudio/test/test_pannerNode.html
+++ b/content/media/webaudio/test/test_pannerNode.html
@@ -42,16 +42,27 @@ addLoadEvent(function() {
   near(panner.rolloffFactor, 1, "Correct default value for rolloff factor");
   near(panner.coneInnerAngle, 360, "Correct default value for cone inner angle");
   near(panner.coneOuterAngle, 360, "Correct default value for cone outer angle");
   near(panner.coneOuterGain, 0, "Correct default value for cone outer gain");
   is(panner.channelCount, 2, "panner node has 2 input channels by default");
   is(panner.channelCountMode, "clamped-max", "Correct channelCountMode for the panner node");
   is(panner.channelInterpretation, "speakers", "Correct channelCountInterpretation for the panner node");
 
+  panner.panningModel = panner.EQUALPOWER;
+  is(panner.panningModel, "equalpower", "Correct alternate panningModel enum value");
+  panner.panningModel = panner.HRTF;
+  is(panner.panningModel, "HRTF", "Correct alternate panningModel enum value");
+  panner.distanceModel = panner.LINEAR_DISTANCE;
+  is(panner.distanceModel, "linear", "Correct alternate distanceModel enum value");
+  panner.distanceModel = panner.INVERSE_DISTANCE;
+  is(panner.distanceModel, "inverse", "Correct alternate distanceModel enum value");
+  panner.distanceModel = panner.EXPONENTIAL_DISTANCE;
+  is(panner.distanceModel, "exponential", "Correct alternate distanceModel enum value");
+
   panner.setPosition(1, 1, 1);
   panner.setOrientation(1, 1, 1);
   panner.setVelocity(1, 1, 1);
 
   source.start(0);
   SimpleTest.executeSoon(function() {
     source.stop(0);
     source.disconnect();
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -77,16 +77,17 @@ MOCHITEST_FILES = \
 		test_SVGxxxList.xhtml \
 		test_SVGxxxListIndexing.xhtml \
 		test_switch.xhtml \
 		switch-helper.svg \
 		test_text.html \
 		test_text_2.html \
 		test_text_scaled.html \
 		test_text_selection.html \
+		test_text_update.html \
 		text-helper.svg \
 		text-helper-scaled.svg \
 		text-helper-selection.svg \
 		test_transform.xhtml \
 		test_valueAsString.xhtml \
 		test_valueLeaks.xhtml \
 		viewport-helper.svg \
 		test_viewport.html \
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_text_update.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Test for Bug 876831</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=876831">Mozilla Bug 876831</a>
+<p id="display"</p>
+
+<!--
+  Test that the frame tree will be reflowed after a DOM mutation
+  and just before an SVG DOM method does its work.
+  -->
+
+<svg>
+  <text>ab</text>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+var text = document.querySelector("text");
+
+var length = text.getComputedTextLength();
+ok(length > 0, "text.getComputedTextLength() > 0");
+
+text.firstChild.nodeValue += "cd";
+ok(text.getComputedTextLength() > length, "text.getComputedTextLength() changes directly after DOM mutation");
+
+text.removeChild(text.firstChild);
+is(text.getComputedTextLength(), 0, "text.getComputedTextLength() == 0 after removing child");
+</script>
+</pre>
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -388,25 +388,23 @@ this.AlarmService = {
     aSuccessCb = aSuccessCb || function() {};
     aErrorCb = aErrorCb || function() {};
 
     if (!aNewAlarm) {
       aErrorCb("alarm is null");
       return;
     }
 
-    aNewAlarm['timezoneOffset'] = this._currentTimezoneOffset;
-    let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
-    if (aNewAlarmTime <= Date.now()) {
-      debug("Adding a alarm that has past time.");
-      this._debugCurrentAlarm();
-      aErrorCb("InvalidStateError");
+    if (!aNewAlarm.date) {
+      aErrorCb("alarm.date is null");
       return;
     }
 
+    aNewAlarm['timezoneOffset'] = this._currentTimezoneOffset;
+
     this._db.add(
       aNewAlarm,
       function addSuccessCb(aNewId) {
         debug("Callback after adding alarm in database.");
 
         aNewAlarm['id'] = aNewId;
 
         // Now that the alarm has been added to the database, we can tack on
@@ -419,16 +417,17 @@ this.AlarmService = {
           this._debugCurrentAlarm();
           aSuccessCb(aNewId);
           return;
         }
 
         // If the new alarm is earlier than the current alarm, swap them and
         // push the previous alarm back to queue.
         let alarmQueue = this._alarmQueue;
+        let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
         let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
         if (aNewAlarmTime < currentAlarmTime) {
           alarmQueue.unshift(this._currentAlarm);
           this._currentAlarm = aNewAlarm;
           this._debugCurrentAlarm();
           aSuccessCb(aNewId);
           return;
         }
--- a/dom/alarm/test/test_alarm_add_date.html
+++ b/dom/alarm/test/test_alarm_add_date.html
@@ -52,21 +52,21 @@
     } catch (e) {
       ok(false,
          "Unexpected exception trying to add alarm for yesterday.");
       return testNull();
     }
     domRequest.onsuccess = function(e) {
       navigator.mozAlarms.remove(e.target.result);
 
-      ok(false, "Should not be able to add alarm for already past date.");
+      ok(true, "Should be able to add alarm for already past date, which should fire immediately.");
       testNull();
     };
     domRequest.onerror = function(e) {
-      ok(true, "Can't use past date when adding new alarm.");
+      ok(false, "Unable to add alarm for yesterday.");
 
       // Errors as it should, on to the next test
       testNull();
     }
   }
 
   // Verify passing null does indeed fail
   function testNull() {
@@ -76,17 +76,17 @@
     } catch(e) {
       ok(false, "Unexpected exception thrown while testing null case.");
 
       // Exception thrown
       return SimpleTest.finish();
     }
     domRequest.onsuccess = function(e) {
       // Null should not be valid
-      ok(false, "Null should not be accepted as input for `respectTimezone` param.");
+      ok(false, "Null should not be accepted as input for `date` param.");
       SimpleTest.finish();
     };
     domRequest.onerror = function(e) {
       // Null should not be valid
       ok(true, "Passing null for date value causes failure.");
 
       SimpleTest.finish();
     };
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1536,16 +1536,45 @@ nsDOMWindowUtils::GetScrollbarSize(bool 
   nsMargin sizes = scrollFrame->GetActualScrollbarSizes();
   *aWidth = nsPresContext::AppUnitsToIntCSSPixels(sizes.LeftRight());
   *aHeight = nsPresContext::AppUnitsToIntCSSPixels(sizes.TopBottom());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetBoundsWithoutFlushing(nsIDOMElement *aElement,
+                                           nsIDOMClientRect** aResult)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+
+  nsresult rv;
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsClientRect> rect = new nsClientRect(window);
+  nsIFrame* frame = content->GetPrimaryFrame();
+
+  if (frame) {
+    nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame,
+               nsLayoutUtils::GetContainingBlockForClientRect(frame),
+               nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
+    rect->SetLayoutRect(r);
+  }
+
+  rect.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetRootBounds(nsIDOMClientRect** aResult)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5577,17 +5577,17 @@ def getEnumValueName(value):
     # 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
     # containing non-ASCII chars for now. Replace all chars other than
     # [0-9A-Za-z_] with '_'.
     if re.match("[^\x20-\x7E]", value):
         raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
     if re.match("^[0-9]", value):
-        raise SyntaxError('Enum value "' + value + '" starts with a digit')
+        return '_' + value
     value = re.sub(r'[^0-9A-Za-z_]', '_', value)
     if re.match("^_[A-Z]|__", value):
         raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
     if value == "_empty":
         raise SyntaxError('"_empty" is not an IDL enum value we support yet')
     if value == "":
         return "_empty"
     return MakeNativeName(value)
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -31,16 +31,17 @@ callback interface TestCallbackInterface
   sequence<TestCallbackInterface?>? getNullableSequenceOfNullableCallbackInterfaces();
 };
 
 callback interface TestSingleOperationCallbackInterface {
   TestInterface doSomething(short arg, sequence<double> anotherArg);
 };
 
 enum TestEnum {
+  "1",
   "a",
   "b"
 };
 
 callback TestCallback = void();
 [TreatNonCallableAsNull] callback TestTreatAsNullCallback = void();
 
 // Callback return value tests
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -302,28 +302,24 @@ public:
       return;
     }
 
     if (!gBluetoothHfpManager) {
       NS_WARNING("BluetoothHfpManager no longer exists, cannot send ring!");
       return;
     }
 
-    nsAutoCString ringMsg(kHfpCrlf);
-    ringMsg.AppendLiteral("RING");
-    ringMsg.AppendLiteral(kHfpCrlf);
+    nsAutoCString ringMsg("RING");
     gBluetoothHfpManager->SendLine(ringMsg.get());
 
     if (!mNumber.IsEmpty()) {
-      nsAutoCString clipMsg(kHfpCrlf);
-      clipMsg.AppendLiteral("+CLIP: \"");
+      nsAutoCString clipMsg("+CLIP: \"");
       clipMsg.Append(NS_ConvertUTF16toUTF8(mNumber).get());
       clipMsg.AppendLiteral("\",");
       clipMsg.AppendInt(mType);
-      clipMsg.AppendLiteral(kHfpCrlf);
       gBluetoothHfpManager->SendLine(clipMsg.get());
     }
 
     MessageLoop::current()->
       PostDelayedTask(FROM_HERE,
                       new SendRingIndicatorTask(mNumber, mType),
                       sRingInterval);
   }
--- a/dom/bluetooth/BluetoothTelephonyListener.cpp
+++ b/dom/bluetooth/BluetoothTelephonyListener.cpp
@@ -62,16 +62,27 @@ TelephonyListener::EnumerateCallState(ui
   *aResult = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyListener::NotifyError(int32_t aCallIndex,
                                const nsAString& aError)
 {
+  BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
+  // In order to not miss any related call state transition.
+  // It's possible that 3G network signal lost for unknown reason.
+  // If a call is released abnormally, NotifyError() will be called,
+  // instead of CallStateChanged(). We need to reset the call array state
+  // via setting CALL_STATE_DISCONNECTED
+  hfp->HandleCallStateChanged(aCallIndex,
+                              nsITelephonyProvider::CALL_STATE_DISCONNECTED,
+                              EmptyString(), false, true);
+  NS_WARNING("Reset the call state due to call transition ends abnormally");
+  NS_WARNING(NS_ConvertUTF16toUTF8(aError).get());
   return NS_OK;
 }
 
 } // anonymous namespace
 
 BluetoothTelephonyListener::BluetoothTelephonyListener()
 {
   mTelephonyListener = new TelephonyListener();
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -34,16 +34,17 @@
 #include "nsDebug.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/ipc/DBusThread.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/Util.h"
+#include "mozilla/NullPtr.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #if defined(MOZ_WIDGET_GONK)
 #include "cutils/properties.h"
 #endif
 
 /**
  * Some rules for dealing with memory in DBus:
  * - A DBusError only needs to be deleted if it's been set, not just
@@ -461,17 +462,17 @@ AgentEventFilter(DBusConnection *conn, D
     // a reply was returned.
 
     // Return directly
     DBusMessage *reply = dbus_message_new_method_return(msg);
 
     if (!reply) {
       errorStr.AssignLiteral("Memory can't be allocated for the message.");
     } else {
-      dbus_connection_send(conn, reply, NULL);
+      dbus_func_send(conn, nullptr, reply);
       dbus_message_unref(reply);
       v = parameters;
     }
   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) {
     // This method gets called when the service daemon needs to authorize a
     // connection/service request.
     char *objectPath;
     const char *uuid;
@@ -613,17 +614,17 @@ AgentEventFilter(DBusConnection *conn, D
     // An agent can use it to do cleanup tasks. There is no need to unregister
     // the agent, because when this method gets called it has already been
     // unregistered.
     DBusMessage *reply = dbus_message_new_method_return(msg);
 
     if (!reply) {
       errorStr.AssignLiteral("Memory can't be allocated for the message.");
     } else {
-      dbus_connection_send(conn, reply, NULL);
+      dbus_func_send(conn, nullptr, reply);
       dbus_message_unref(reply);
 
       // Do not send an notification to upper layer, too annoying.
       return DBUS_HANDLER_RESULT_HANDLED;
     }
   } else {
 #ifdef DEBUG
     BT_WARNING("agent handler %s: Unhandled event. Ignore.", __FUNCTION__);
@@ -683,38 +684,35 @@ RegisterLocalAgent(const char* adapterPa
   if (!dbus_message_append_args(msg,
                                 DBUS_TYPE_OBJECT_PATH, &agentPath,
                                 DBUS_TYPE_STRING, &capabilities,
                                 DBUS_TYPE_INVALID)) {
     BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
     return false;
   }
 
-  DBusError err;
-  dbus_error_init(&err);
-
-  DBusMessage* reply =
-    dbus_connection_send_with_reply_and_block(gThreadConnection->GetConnection(),
-                                              msg, -1, &err);
-  dbus_message_unref(msg);
-
-  if (!reply) {
+  DBusError err = DBUS_ERROR_INIT;
+  DBusMessage* reply;
+
+  dbus_bool_t success = dbus_func_send_and_block(gThreadConnection->GetConnection(),
+                                                 -1, &reply, &err, msg);
+  if (!success) {
     if (dbus_error_is_set(&err)) {
       if (!strcmp(err.name, "org.bluez.Error.AlreadyExists")) {
         LOG_AND_FREE_DBUS_ERROR(&err);
 #ifdef DEBUG
         BT_WARNING("Agent already registered, still returning true");
 #endif
       } else {
         LOG_AND_FREE_DBUS_ERROR(&err);
         BT_WARNING("%s: Can't register agent!", __FUNCTION__);
         return false;
       }
     }
-  } else {
+  } else if (reply) {
     dbus_message_unref(reply);
   }
 
   dbus_connection_flush(gThreadConnection->GetConnection());
   return true;
 }
 
 static bool
@@ -900,24 +898,17 @@ void
 RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable,
                 UnpackFunc aFunc)
 {
 #ifdef MOZ_WIDGET_GONK
   // Due to the fact that we're running two dbus loops on desktop implicitly by
   // being gtk based, sometimes we'll get signals/reply coming in on the main
   // thread. There's not a lot we can do about that for the time being and it
   // (technically) shouldn't hurt anything. However, on gonk, die.
-
-  // Due to the fact introducing workaround in Bug 827888, the callback for a
-  // message gets executed immediately. The proper fix is in bug 830290, but
-  // it's a intrusive change, it is better to remove assertion here since it
-  // would not hurt anything.
-  // Tracking bug 830290 for intrusive solution.
-
-  // MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!NS_IsMainThread());
 #endif
   nsRefPtr<BluetoothReplyRunnable> replyRunnable =
     dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable));
 
   NS_ASSERTION(replyRunnable, "Callback reply runnable is null!");
 
   nsAutoString replyError;
   BluetoothValue v;
@@ -2353,17 +2344,17 @@ BluetoothDBusService::SetPinCodeInternal
 
   if (!dbus_message_append_args(reply,
                                 DBUS_TYPE_STRING, &pinCode,
                                 DBUS_TYPE_INVALID)) {
     BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
     errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
     result = false;
   } else {
-    result = dbus_connection_send(mConnection, reply, NULL);
+    result = dbus_func_send(mConnection, nullptr, reply);
   }
 
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable.Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
   return result;
@@ -2399,17 +2390,17 @@ BluetoothDBusService::SetPasskeyInternal
 
   if (!dbus_message_append_args(reply,
                                 DBUS_TYPE_UINT32, &passkey,
                                 DBUS_TYPE_INVALID)) {
     BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
     errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
     result = false;
   } else {
-    result = dbus_connection_send(mConnection, reply, NULL);
+    result = dbus_func_send(mConnection, nullptr, reply);
   }
 
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable.Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
   return result;
@@ -2443,17 +2434,17 @@ BluetoothDBusService::SetPairingConfirma
   if (!reply) {
     BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
     dbus_message_unref(msg);
     errorStr.AssignLiteral("Memory can't be allocated for the message.");
     DispatchBluetoothReply(aRunnable, v, errorStr);
     return false;
   }
 
-  bool result = dbus_connection_send(mConnection, reply, NULL);
+  bool result = dbus_func_send(mConnection, nullptr, reply);
   if (!result) {
     errorStr.AssignLiteral("Can't send message!");
   }
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable.Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
@@ -2489,17 +2480,17 @@ BluetoothDBusService::SetAuthorizationIn
   if (!reply) {
     BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
     dbus_message_unref(msg);
     errorStr.AssignLiteral("Memory can't be allocated for the message.");
     DispatchBluetoothReply(aRunnable, v, errorStr);
     return false;
   }
 
-  bool result = dbus_connection_send(mConnection, reply, NULL);
+  bool result = dbus_func_send(mConnection, nullptr, reply);
   if (!result) {
     errorStr.AssignLiteral("Can't send message!");
   }
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sAuthorizeReqTable.Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
--- a/dom/browser-element/mochitest/browserElement_LoadEvents.js
+++ b/dom/browser-element/mochitest/browserElement_LoadEvents.js
@@ -40,16 +40,17 @@ function runTest() {
     ok(!seenLoadEnd, 'Location change before load end.');
     ok(e.detail, browserElementTestHelpers.emptyPage1, "event's reported location");
   }
 
   function loadend(e) {
     ok(e.isTrusted, 'Event should be trusted.');
     ok(seenLoadStart, 'loadend after loadstart.');
     ok(!seenLoadEnd, 'Just one loadend event.');
+    ok(seenLocationChange, 'loadend after locationchange.');
     seenLoadEnd = true;
   }
 
   iframe.addEventListener('mozbrowserloadstart', loadstart);
   iframe.addEventListener('mozbrowserlocationchange', locationchange);
   iframe.addEventListener('mozbrowserloadend', loadend);
 
   function waitForAllCallbacks() {
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -200,16 +200,49 @@ ContactFindOptions.prototype = {
 }
 
 //Contact
 
 const CONTACT_CONTRACTID = "@mozilla.org/contact;1";
 const CONTACT_CID        = Components.ID("{72a5ee28-81d8-4af8-90b3-ae935396cc66}");
 const nsIDOMContact      = Components.interfaces.nsIDOMContact;
 
+function checkBlobArray(aBlob) {
+  if (Array.isArray(aBlob)) {
+    for (let i = 0; i < aBlob.length; i++) {
+      if (typeof aBlob != 'object') {
+        return null;
+      }
+      if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) {
+        return null;
+      }
+    }
+    return aBlob;
+  }
+  return null;
+}
+
+function isVanillaObj(aObj) {
+  return Object.prototype.toString.call(aObj) == "[object Object]";
+}
+
+function validateArrayField(data, createCb) {
+  if (data) {
+    data = Array.isArray(data) ? data : [data];
+    let filtered = [];
+    for (let obj of data) {
+      if (obj && isVanillaObj(obj)) {
+        filtered.push(createCb(obj));
+      }
+    }
+    return filtered;
+  }
+  return undefined;
+}
+
 function Contact() { };
 
 Contact.prototype = {
   __exposedProps__: {
                       id: 'rw',
                       updated: 'rw',
                       published:  'rw',
                       name: 'rw',
@@ -230,130 +263,230 @@ Contact.prototype = {
                       bday: 'rw',
                       note: 'rw',
                       impp: 'rw',
                       anniversary: 'rw',
                       sex: 'rw',
                       genderIdentity: 'rw'
                      },
 
-  init: function init(aProp) {
-    function _checkBlobArray(aBlob) {
-      if (Array.isArray(aBlob)) {
-        for (let i = 0; i < aBlob.length; i++) {
-          if (typeof aBlob != 'object') {
-            return null;
-          }
-          if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) {
-            return null;
-          }
-        }
-        return aBlob;
-      }
-      return null;
-    }
+  set name(aName) {
+    this._name = sanitizeStringArray(aName);
+  },
+
+  get name() {
+    return this._name;
+  },
+
+  set honorificPrefix(aHonorificPrefix) {
+    this._honorificPrefix = sanitizeStringArray(aHonorificPrefix);
+  },
+
+  get honorificPrefix() {
+    return this._honorificPrefix;
+  },
+
+  set givenName(aGivenName) {
+    this._givenName = sanitizeStringArray(aGivenName);
+  },
+
+  get givenName() {
+    return this._givenName;
+  },
 
-    function _isVanillaObj(aObj) {
-      return Object.prototype.toString.call(aObj) == "[object Object]";
-    }
+  set additionalName(aAdditionalName) {
+    this._additionalName = sanitizeStringArray(aAdditionalName);
+  },
+
+  get additionalName() {
+    return this._additionalName;
+  },
+
+  set familyName(aFamilyName) {
+    this._familyName = sanitizeStringArray(aFamilyName);
+  },
 
-    let _create = sanitizeStringArray;
+  get familyName() {
+    return this._familyName;
+  },
+
+  set honorificSuffix(aHonorificSuffix) {
+    this._honorificSuffix = sanitizeStringArray(aHonorificSuffix);
+  },
+
+  get honorificSuffix() {
+    return this._honorificSuffix;
+  },
+
+  set nickname(aNickname) {
+    this._nickname = sanitizeStringArray(aNickname);
+  },
 
-    this.name =            _create(aProp.name);
-    this.honorificPrefix = _create(aProp.honorificPrefix);
-    this.givenName =       _create(aProp.givenName);
-    this.additionalName =  _create(aProp.additionalName);
-    this.familyName =      _create(aProp.familyName);
-    this.honorificSuffix = _create(aProp.honorificSuffix);
-    this.nickname =        _create(aProp.nickname);
+  get nickname() {
+    return this._nickname;
+  },
+
+  set photo(aPhoto) {
+    this._photo = checkBlobArray(aPhoto);
+  },
+
+  get photo() {
+    return this._photo;
+  },
+
+  set category(aCategory) {
+    this._category = sanitizeStringArray(aCategory);
+  },
+
+  get category() {
+    return this._category;
+  },
+
+  set email(aEmail) {
+    this._email = validateArrayField(aEmail, function(email) {
+      return new ContactField(email.type, email.value, email.pref);
+    });
+  },
 
-    if (aProp.email) {
-      aProp.email = Array.isArray(aProp.email) ? aProp.email : [aProp.email];
-      this.email = new Array();
-      for (let email of aProp.email) {
-        if (_isVanillaObj(email)) {
-          this.email.push(new ContactField(email.type, email.value, email.pref));
-        } else if (DEBUG) {
-          debug("email field is not a ContactField and was ignored.");
-        }
-      }
-    } else if (DEBUG) {
-      this.email = null;
-    }
+  get email() {
+    return this._email;
+  },
+
+  set adr(aAdr) {
+    this._adr = validateArrayField(aAdr, function(adr) {
+      return new ContactAddress(adr.type, adr.streetAddress, adr.locality,
+                                adr.region, adr.postalCode, adr.countryName,
+                                adr.pref);
+    });
+  },
 
-    this.photo =           _checkBlobArray(aProp.photo);
-    this.category =        _create(aProp.category);
+  get adr() {
+    return this._adr;
+  },
+
+  set tel(aTel) {
+    this._tel = validateArrayField(aTel, function(tel) {
+      return new ContactTelField(tel.type, tel.value, tel.carrier, tel.pref);
+    });
+  },
+
+  get tel() {
+    return this._tel;
+  },
 
-    if (aProp.adr) {
-      aProp.adr = Array.isArray(aProp.adr) ? aProp.adr : [aProp.adr];
-      this.adr = new Array();
-      for (let adr of aProp.adr) {
-        if (_isVanillaObj(adr)) {
-          this.adr.push(new ContactAddress(adr.type, adr.streetAddress, adr.locality,
-                                           adr.region, adr.postalCode, adr.countryName,
-                                           adr.pref));
-        } else if (DEBUG) {
-          debug("adr field is not a ContactAddress and was ignored.");
-        }
-      }
-    } else {
-      this.adr = null;
+  set impp(aImpp) {
+    this._impp = validateArrayField(aImpp, function(impp) {
+      return new ContactField(impp.type, impp.value, impp.pref);
+    });
+  },
+
+  get impp() {
+    return this._impp;
+  },
+
+  set url(aUrl) {
+    this._url = validateArrayField(aUrl, function(url) {
+      return new ContactField(url.type, url.value, url.pref);
+    });
+  },
+
+  get url() {
+    return this._url;
+  },
+
+  set org(aOrg) {
+    this._org = sanitizeStringArray(aOrg);
+  },
+
+  get org() {
+    return this._org;
+  },
+
+  set jobTitle(aJobTitle) {
+    this._jobTitle = sanitizeStringArray(aJobTitle);
+  },
+
+  get jobTitle() {
+    return this._jobTitle;
+  },
+
+  set note(aNote) {
+    this._note = sanitizeStringArray(aNote);
+  },
+
+  get note() {
+    return this._note;
+  },
+
+  set bday(aBday) {
+    if (aBday !== undefined && aBday !== null) {
+      this._bday = new Date(aBday);
     }
+  },
 
-    if (aProp.tel) {
-      aProp.tel = Array.isArray(aProp.tel) ? aProp.tel : [aProp.tel];
-      this.tel = new Array();
-      for (let tel of aProp.tel) {
-        if (_isVanillaObj(tel)) {
-          this.tel.push(new ContactTelField(tel.type, tel.value, tel.carrier,
-                                            tel.pref));
-        } else if (DEBUG) {
-          debug("tel field is not a ContactTelField and was ignored.");
-        }
-      }
-    } else {
-      this.tel = null;
-    }
+  get bday() {
+    return this._bday;
+  },
 
-    this.org =             _create(aProp.org);
-    this.jobTitle =        _create(aProp.jobTitle);
-    this.bday =            (aProp.bday == undefined || aProp.bday == null) ? null : new Date(aProp.bday);
-    this.note =            _create(aProp.note);
+  set anniversary(aAnniversary) {
+    if (aAnniversary !== undefined && aAnniversary !== null) {
+      this._anniversary = new Date(aAnniversary);
+    }
+  },
+
+  get anniversary() {
+    return this._anniversary;
+  },
 
-    if (aProp.impp) {
-      aProp.impp = Array.isArray(aProp.impp) ? aProp.impp : [aProp.impp];
-      this.impp = new Array();
-      for (let impp of aProp.impp) {
-        if (_isVanillaObj(impp)) {
-          this.impp.push(new ContactField(impp.type, impp.value, impp.pref));
-        } else if (DEBUG) {
-          debug("impp field is not a ContactField and was ignored.");
-        }
-      }
+  set sex(aSex) {
+    if (aSex !== "undefined") {
+      this._sex = aSex;
     } else {
-      this.impp = null;
+      this._sex = null;
+    }
+  },
+
+  get sex() {
+    return this._sex;
+  },
+
+  set genderIdentity(aGenderIdentity) {
+    if (aGenderIdentity !== "undefined") {
+      this._genderIdentity = aGenderIdentity;
+    } else {
+      this._genderIdentity = null;
     }
+  },
 
-    if (aProp.url) {
-      aProp.url = Array.isArray(aProp.url) ? aProp.url : [aProp.url];
-      this.url = new Array();
-      for (let url of aProp.url) {
-        if (_isVanillaObj(url)) {
-          this.url.push(new ContactField(url.type, url.value, url.pref));
-        } else if (DEBUG) {
-          debug("url field is not a ContactField and was ignored.");
-        }
-      }
-    } else {
-      this.url = null;
-    }
+  get genderIdentity() {
+    return this._genderIdentity;
+  },
 
-    this.anniversary =     (aProp.anniversary == undefined || aProp.anniversary == null) ? null : new Date(aProp.anniversary);
-    this.sex =             (aProp.sex != "undefined") ? aProp.sex : null;
-    this.genderIdentity =  (aProp.genderIdentity != "undefined") ? aProp.genderIdentity : null;
+  init: function init(aProp) {
+    this.name =            aProp.name;
+    this.honorificPrefix = aProp.honorificPrefix;
+    this.givenName =       aProp.givenName;
+    this.additionalName =  aProp.additionalName;
+    this.familyName =      aProp.familyName;
+    this.honorificSuffix = aProp.honorificSuffix;
+    this.nickname =        aProp.nickname;
+    this.email =           aProp.email;
+    this.photo =           aProp.photo;
+    this.url =             aProp.url;
+    this.category =        aProp.category;
+    this.adr =             aProp.adr;
+    this.tel =             aProp.tel;
+    this.org =             aProp.org;
+    this.jobTitle =        aProp.jobTitle;
+    this.bday =            aProp.bday;
+    this.note =            aProp.note;
+    this.impp =            aProp.impp;
+    this.anniversary =     aProp.anniversary;
+    this.sex =             aProp.sex;
+    this.genderIdentity =  aProp.genderIdentity;
   },
 
   get published () {
     return this._published;
   },
 
   set published(aPublished) {
     this._published = aPublished;
@@ -375,17 +508,17 @@ Contact.prototype = {
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMContact, nsIContactProperties])
 }
 
 // ContactManager
 
 const CONTACTMANAGER_CONTRACTID = "@mozilla.org/contactManager;1";
-const CONTACTMANAGER_CID        = Components.ID("{7bfb6481-f946-4254-afc5-d7fe9f5c45a3}");
+const CONTACTMANAGER_CID        = Components.ID("{8beb3a66-d70a-4111-b216-b8e995ad3aff}");
 const nsIDOMContactManager      = Components.interfaces.nsIDOMContactManager;
 
 function ContactManager()
 {
   if (DEBUG) debug("Constructor");
 }
 
 ContactManager.prototype = {
@@ -520,16 +653,23 @@ ContactManager.prototype = {
         break;
       case "Contacts:Revision":
         if (DEBUG) debug("new revision: " + msg.revision);
         req = this.getRequest(msg.requestID);
         if (req) {
           Services.DOMRequest.fireSuccess(req, msg.revision);
         }
         break;
+      case "Contacts:Count":
+        if (DEBUG) debug("count: " + msg.count);
+        req = this.getRequest(msg.requestID);
+        if (req) {
+          Services.DOMRequest.fireSuccess(req, msg.count);
+        }
+        break;
       default:
         if (DEBUG) debug("Wrong message: " + aMessage.name);
     }
     this.removeRequest(msg.requestID);
   },
 
   askPermission: function (aAccess, aRequest, aAllowCallback, aCancelCallback) {
     if (DEBUG) debug("askPermission for contacts");
@@ -540,16 +680,17 @@ ContactManager.prototype = {
         break;
       case "update":
       case "remove":
         access = "write";
         break;
       case "find":
       case "listen":
       case "revision":
+      case "count":
         access = "read";
         break;
       default:
         access = "unknown";
       }
 
     // Shortcut for ALLOW_ACTION so we avoid a parent roundtrip
     let type = "contacts-" + access;
@@ -729,25 +870,42 @@ ContactManager.prototype = {
     let cancelCallback = function() {
       Services.DOMRequest.fireError(request);
     };
 
     this.askPermission("revision", request, allowCallback, cancelCallback);
     return request;
   },
 
+  getCount: function() {
+    let request = this.createRequest();
+
+    let allowCallback = function() {
+      cpmm.sendAsyncMessage("Contacts:GetCount", {
+        requestID: this.getRequestId(request)
+      });
+    }.bind(this);
+
+    let cancelCallback = function() {
+      Services.DOMRequest.fireError(request);
+    };
+
+    this.askPermission("count", request, allowCallback, cancelCallback);
+    return request;
+  },
+
   init: function(aWindow) {
     this.initHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
                               "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
                               "Contact:Save:Return:OK", "Contact:Save:Return:KO",
                               "Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
                               "Contact:Changed",
                               "PermissionPromptHelper:AskPermission:OK",
-                              "Contacts:GetAll:Next",
-                              "Contacts:Revision"]);
+                              "Contacts:GetAll:Next", "Contacts:Revision",
+                              "Contacts:Count"]);
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) debug("uninit call");
     if (this._oncontactchange)
       this._oncontactchange = null;
   },
--- a/dom/contacts/ContactManager.manifest
+++ b/dom/contacts/ContactManager.manifest
@@ -15,11 +15,11 @@ contract @mozilla.org/contactFindSortOpt
 
 component {28ce07d0-45d9-4b7a-8843-521df4edd8bc} ContactManager.js
 contract @mozilla.org/contactFindOptions;1 {28ce07d0-45d9-4b7a-8843-521df4edd8bc}
 
 component {72a5ee28-81d8-4af8-90b3-ae935396cc66} ContactManager.js
 contract @mozilla.org/contact;1 {72a5ee28-81d8-4af8-90b3-ae935396cc66}
 category JavaScript-global-constructor mozContact @mozilla.org/contact;1
 
-component {7bfb6481-f946-4254-afc5-d7fe9f5c45a3} ContactManager.js
-contract @mozilla.org/contactManager;1 {7bfb6481-f946-4254-afc5-d7fe9f5c45a3}
+component {8beb3a66-d70a-4111-b216-b8e995ad3aff} ContactManager.js
+contract @mozilla.org/contactManager;1 {8beb3a66-d70a-4111-b216-b8e995ad3aff}
 category JavaScript-navigator-property mozContacts @mozilla.org/contactManager;1
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -11,16 +11,17 @@ function debug(s) { dump("-*- ContactDB 
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
+Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const DB_NAME = "contacts";
 const DB_VERSION = 11;
 const STORE_NAME = "contacts";
 const SAVED_GETALL_STORE_NAME = "getallcache";
 const CHUNK_SIZE = 20;
 const REVISION_STORE = "revision";
 const REVISION_KEY = "revision";
@@ -721,59 +722,75 @@ ContactDB.prototype = {
     }.bind(this));
   },
 
   getRevision: function CDB_getRevision(aSuccessCb) {
     if (DEBUG) debug("getRevision");
     this.newTxn("readonly", REVISION_STORE, function (txn, store) {
       store.get(REVISION_KEY).onsuccess = function (e) {
         aSuccessCb(e.target.result);
-      }
+      };
+    });
+  },
+
+  getCount: function CDB_getCount(aSuccessCb) {
+    if (DEBUG) debug("getCount");
+    this.newTxn("readonly", STORE_NAME, function (txn, store) {
+      store.count().onsuccess = function (e) {
+        aSuccessCb(e.target.result);
+      };
     });
   },
 
   /*
    * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName.
    * If 2 entries have the same sortyBy field or no sortBy field is present, we continue
    * sorting with the other sortyBy field.
    */
   sortResults: function CDB_sortResults(aResults, aFindOptions) {
     if (!aFindOptions)
       return;
     if (aFindOptions.sortBy != "undefined") {
+      const sortOrder = aFindOptions.sortOrder;
+      const sortBy = aFindOptions.sortBy == "familyName" ? [ "familyName", "givenName" ] : [ "givenName" , "familyName" ];
+
       aResults.sort(function (a, b) {
         let x, y;
         let result = 0;
-        let sortOrder = aFindOptions.sortOrder;
-        let sortBy = aFindOptions.sortBy == "familyName" ? [ "familyName", "givenName" ] : [ "givenName" , "familyName" ];
         let xIndex = 0;
         let yIndex = 0;
 
         do {
           while (xIndex < sortBy.length && !x) {
-            x = a.properties[sortBy[xIndex]] && a.properties[sortBy[xIndex]][0] ? a.properties[sortBy[xIndex]][0].toLowerCase() : null;
+            x = a.properties[sortBy[xIndex]];
+            if (x) {
+              x = x.join("").toLowerCase();
+            }
             xIndex++;
           }
           if (!x) {
-            return sortOrder == 'descending' ? 1 : -1;
+            return sortOrder == "descending" ? 1 : -1;
           }
           while (yIndex < sortBy.length && !y) {
-            y = b.properties[sortBy[yIndex]] && b.properties[sortBy[yIndex]][0] ? b.properties[sortBy[yIndex]][0].toLowerCase() : null;
+            y = b.properties[sortBy[yIndex]];
+            if (y) {
+              y = y.join("").toLowerCase();
+            }
             yIndex++;
           }
           if (!y) {
-            return sortOrder == 'ascending' ? 1 : -1;
+            return sortOrder == "ascending" ? 1 : -1;
           }
 
           result = x.localeCompare(y);
           x = null;
           y = null;
         } while (result == 0);
 
-        return sortOrder == 'ascending' ? result : -result;
+        return sortOrder == "ascending" ? result : -result;
       });
     }
     if (aFindOptions.filterLimit && aFindOptions.filterLimit != 0) {
       if (DEBUG) debug("filterLimit is set: " + aFindOptions.filterLimit);
       aResults.splice(aFindOptions.filterLimit, aResults.length);
     }
   },
 
@@ -788,17 +805,18 @@ ContactDB.prototype = {
    *        - filterOp
    *        - filterValue
    *        - count
    */
   find: function find(aSuccessCb, aFailureCb, aOptions) {
     if (DEBUG) debug("ContactDB:find val:" + aOptions.filterValue + " by: " + aOptions.filterBy + " op: " + aOptions.filterOp);
     let self = this;
     this.newTxn("readonly", STORE_NAME, function (txn, store) {
-      if (aOptions && (["equals", "contains", "match"].indexOf(aOptions.filterOp) >= 0)) {
+      let filterOps = ["equals", "contains", "match", "startsWith"];
+      if (aOptions && (filterOps.indexOf(aOptions.filterOp) >= 0)) {
         self._findWithIndex(txn, store, aOptions);
       } else {
         self._findAll(txn, store, aOptions);
       }
     }, aSuccessCb, aFailureCb);
   },
 
   _findWithIndex: function _findWithIndex(txn, store, options) {
@@ -834,34 +852,41 @@ ContactDB.prototype = {
         let index = store.index(key);
         request = index.mozGetAll(options.filterValue, limit);
       } else if (options.filterOp == "equals") {
         if (DEBUG) debug("Getting index: " + key);
         // case sensitive
         let index = store.index(key);
         let filterValue = options.filterValue;
         if (key == "tel") {
-          filterValue = PhoneNumberUtils.normalize(filterValue);
+          filterValue = PhoneNumberUtils.normalize(filterValue,
+                                                   /*numbersOnly*/ true);
         }
         request = index.mozGetAll(filterValue, limit);
       } else if (options.filterOp == "match") {
         if (DEBUG) debug("match");
         if (key != "tel") {
           dump("ContactDB: 'match' filterOp only works on tel\n");
           return txn.abort();
         }
 
         let index = store.index("telMatch");
-        let normalized = PhoneNumberUtils.normalize(options.filterValue)
+        let normalized = PhoneNumberUtils.normalize(options.filterValue,
+                                                    /*numbersOnly*/ true);
         request = index.mozGetAll(normalized, limit);
       } else {
+        // XXX: "contains" should be handled separately, this is "startsWith"
+        if (options.filterOp === 'contains' && key !== 'tel') {
+          console.warn("ContactDB: 'contains' only works for 'tel'. " +
+                       "Falling back to 'startsWith'.");
+        }
         // not case sensitive
         let tmp = options.filterValue.toString().toLowerCase();
-        if (key === 'tel') {
-          tmp = PhoneNumberUtils.normalize(tmp);
+        if (key === "tel") {
+          tmp = PhoneNumberUtils.normalize(tmp, /*numbersOnly*/ true);
         }
         let range = this._global.IDBKeyRange.bound(tmp, tmp + "\uFFFF");
         let index = store.index(key + "LowerCase");
         request = index.mozGetAll(range, limit);
       }
       if (!txn.result)
         txn.result = {};
 
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -24,17 +24,18 @@ XPCOMUtils.defineLazyServiceGetter(this,
 let myGlobal = this;
 
 let ContactService = {
   init: function() {
     if (DEBUG) debug("Init");
     this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:SendNow",
                       "Contacts:Clear", "Contact:Save",
                       "Contact:Remove", "Contacts:RegisterForMessages",
-                      "child-process-shutdown", "Contacts:GetRevision"];
+                      "child-process-shutdown", "Contacts:GetRevision",
+                      "Contacts:GetCount"];
     this._children = [];
     this._cursors = {};
     this._messages.forEach(function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
     var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
     idbManager.initWindowless(myGlobal);
@@ -179,16 +180,29 @@ let ContactService = {
           function(revision) {
             mm.sendAsyncMessage("Contacts:Revision", {
               requestID: msg.requestID,
               revision: revision
             });
           }
         );
         break;
+      case "Contacts:GetCount":
+        if (!this.assertPermission(aMessage, "contacts-read")) {
+          return null;
+        }
+        this._db.getCount(
+          function(count) {
+            mm.sendAsyncMessage("Contacts:Count", {
+              requestID: msg.requestID,
+              count: count
+            });
+          }
+        );
+        break;
       case "Contacts:RegisterForMessages":
         if (!aMessage.target.assertPermission("contacts-read")) {
           return null;
         }
         if (DEBUG) debug("Register!");
         if (this._children.indexOf(mm) == -1) {
           this._children.push(mm);
         }
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -40,24 +40,24 @@ var c1 = {
 var c2 = {
   name: "b",
   familyName: ["b"],
   givenName: ["b"],
 };
 
 var c3 = {
   name: "c",
-  familyName: ["c","x"],
-  givenName: ["c","x"],
+  familyName: ["c","a","b"],
+  givenName: ["c","a","b"],
 };
 
 var c4 = {
   name: "d",
-  familyName: ["d","e"],
-  givenName: ["d","e"],
+  familyName: ["c","a","c"],
+  givenName: ["c","a","c"],
 };
 
 var c5 = {
   name: "empty"
 };
 
 var adr1 = {
   type: "work",
@@ -132,111 +132,102 @@ function onUnwantedSuccess() {
   ok(false, "onUnwantedSuccess: shouldn't get here");
 }
 
 function onFailure() {
   ok(false, "in on Failure!");
 }
 
 function checkStr(str1, str2, msg) {
+  if (str1 ^ str2) {
+    ok(false, "Expected both strings to be either present or absent");
+    return;
+  }
+  is(str1, str2, msg);
+}
+
+function checkStrArray(str1, str2, msg) {
   // comparing /[null(,null)+]/ and undefined should pass
   function nonNull(e) {
     return e != null;
   }
   if ((Array.isArray(str1) && str1.filter(nonNull).length == 0 && str2 == undefined)
      ||(Array.isArray(str2) && str2.filter(nonNull).length == 0 && str1 == undefined)) {
     ok(true, msg);
   } else if (str1) {
     is(JSON.stringify(typeof str1 == "string" ? [str1] : str1), JSON.stringify(typeof str2 == "string" ? [str2] : str2), msg);
   }
 }
 
 function checkAddress(adr1, adr2) {
-  checkStr(adr1.type, adr2.type, "Same type");
-  checkStr(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
-  checkStr(adr1.locality, adr2.locality, "Same locality");
-  checkStr(adr1.region, adr2.region, "Same region");
-  checkStr(adr1.postalCode, adr2.postalCode, "Same postalCode");
-  checkStr(adr1.countryName, adr2.countryName, "Same countryName");
+  if (adr1 ^ adr2) {
+    ok(false, "Expected both adrs to be either present or absent");
+    return;
+  }
+  dump("adr1: " + adr1 + ", adr2: " + adr2 + "\n");
+  checkStrArray(adr1.type, adr2.type, "Same type");
+  checkStrArray(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
+  checkStrArray(adr1.locality, adr2.locality, "Same locality");
+  checkStrArray(adr1.region, adr2.region, "Same region");
+  checkStrArray(adr1.postalCode, adr2.postalCode, "Same postalCode");
+  checkStrArray(adr1.countryName, adr2.countryName, "Same countryName");
   is(adr1.pref, adr2.pref, "Same pref");
 }
 
 function checkTel(tel1, tel2) {
-  checkStr(tel1.type, tel2.type, "Same type");
-  checkStr(tel1.value, tel2.value, "Same value");
-  checkStr(tel1.carrier, tel2.carrier, "Same carrier");
+  if (tel1 ^ tel2) {
+    ok(false, "Expected both tels to be either present or absent");
+    return;
+  }
+  checkStrArray(tel1.type, tel2.type, "Same type");
+  checkStrArray(tel1.value, tel2.value, "Same value");
+  checkStrArray(tel1.carrier, tel2.carrier, "Same carrier");
   is(tel1.pref, tel2.pref, "Same pref");
 }
 
 function checkField(field1, field2) {
-  checkStr(field1.type, field2.type, "Same type");
-  checkStr(field1.value, field2.value, "Same value");
+  if (field1 ^ field2) {
+    ok(false, "Expected both fields to be either present or absent");
+    return;
+  }
+  checkStrArray(field1.type, field2.type, "Same type");
+  checkStrArray(field1.value, field2.value, "Same value");
   is(field1.pref, field2.pref, "Same pref");
 }
 
 function checkContacts(contact1, contact2) {
-  checkStr(contact1.name, contact2.name, "Same name");
-  checkStr(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
-  checkStr(contact1.givenName, contact2.givenName, "Same givenName");
-  checkStr(contact1.additionalName, contact2.additionalName, "Same additionalName");
-  checkStr(contact1.familyName, contact2.familyName, "Same familyName");
-  checkStr(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
-  checkStr(contact1.nickname, contact2.nickname, "Same nickname");
-  checkStr(contact1.category, contact2.category, "Same category");
-  checkStr(contact1.org, contact2.org, "Same org");
-  checkStr(contact1.jobTitle, contact2.jobTitle, "Same jobTitle");
+  checkStrArray(contact1.name, contact2.name, "Same name");
+  checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
+  checkStrArray(contact1.givenName, contact2.givenName, "Same givenName");
+  checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName");
+  checkStrArray(contact1.familyName, contact2.familyName, "Same familyName");
+  checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
+  checkStrArray(contact1.nickname, contact2.nickname, "Same nickname");
+  checkStrArray(contact1.category, contact2.category, "Same category");
+  checkStrArray(contact1.org, contact2.org, "Same org");
+  checkStrArray(contact1.jobTitle, contact2.jobTitle, "Same jobTitle");
   is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday");
-  checkStr(contact1.note, contact2.note, "Same note");
+  checkStrArray(contact1.note, contact2.note, "Same note");
   is(contact1.anniversary ? contact1.anniversary.valueOf() : null , contact2.anniversary ? contact2.anniversary.valueOf() : null, "Same anniversary");
-  is(contact1.sex, contact2.sex, "Same sex");
-  is(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
+  checkStr(contact1.sex, contact2.sex, "Same sex");
+  checkStr(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
 
   for (var i in contact1.email) {
-    if (contact1.email) {
-      ok(contact2.email != null, "conatct2.email exists");
-    }
-    if (contact2.email) {
-      ok(contact1.email != null, "conatct1.email exists");
-    }
     checkField(contact1.email[i], contact2.email[i]);
   }
   for (var i in contact1.adr) {
-    if (contact1.adr) {
-      ok(contact2.adr != null, "conatct2.adr exists");
-    }
-    if (contact2.adr) {
-      ok(contact1.adr != null, "conatct1.adr exists");
-    }
     checkAddress(contact1.adr[i], contact2.adr[i]);
   }
   for (var i in contact1.tel) {
-    if (contact1.tel) {
-      ok(contact2.tel != null, "conatct2.tel exists");
-    }
-    if (contact2.tel) {
-      ok(contact1.tel != null, "conatct1.tel exists");
-    }
     checkTel(contact1.tel[i], contact2.tel[i]);
   }
   for (var i in contact1.url) {
-    if (contact1.url) {
-      ok(contact2.url != null, "conatct2.url exists");
-    }
-    if (contact2.url) {
-      ok(contact1.url != null, "conatct1.url exists");
-    }
     checkField(contact1.url[i], contact2.url[i]);
   }
   for (var i in contact1.impp) {
-    if (contact1.impp) {
-      ok(contact2.impp != null, "conatct2.impp exists");
-    }
-    if (contact2.impp) {
-      ok(contact1.impp != null, "conatct1.impp exists");
-    }
     checkField(contact1.impp[i], contact2.impp[i]);
   }
 }
 
 var req;
 var index = 0;
 
 var initialRev;
@@ -245,33 +236,44 @@ function checkRevision(revision, msg, th
   var revReq = mozContacts.getRevision();
   revReq.onsuccess = function(e) {
     is(e.target.result, initialRev+revision, msg);
     then();
   };
   revReq.onerror = onFailure;
 }
 
+function checkCount(count, msg, then) {
+  var request = navigator.mozContacts.getCount();
+  request.onsuccess = function(e) {
+    is(e.target.result, count, msg);
+    then();
+  };
+  request.onerror = onFailure;
+}
+
 var mozContacts = window.navigator.mozContacts;
 ok(mozContacts, "mozContacts exists");
 ok("mozContact" in window, "mozContact exists");
 var steps = [
   function() {
     mozContacts.getRevision().onsuccess = function(e) {
       initialRev = e.target.result;
       next();
     };
   },
   function () {
     ok(true, "Deleting database");
     checkRevision(0, "Initial revision is 0", function() {
       req = mozContacts.clear();
       req.onsuccess = function () {
         ok(true, "Deleted the database");
-        checkRevision(1, "Revision was incremented on clear", next);
+        checkCount(0, "No contacts after clear", function() {
+          checkRevision(1, "Revision was incremented on clear", next);
+        });
       };
       req.onerror = onFailure;
     });
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
@@ -283,17 +285,19 @@ var steps = [
   function () {
     ok(true, "Adding empty contact");
     createResult1 = new mozContact();
     createResult1.init({});
     req = navigator.mozContacts.save(createResult1);
     req.onsuccess = function () {
       ok(createResult1.id, "The contact now has an ID.");
       sample_id1 = createResult1.id;
-      checkRevision(2, "Revision was incremented on save", next);
+      checkCount(1, "1 contact after adding empty contact", function() {
+        checkRevision(2, "Revision was incremented on save", next);
+      });
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
       is(req.result.length, 1, "One contact.");
@@ -326,24 +330,24 @@ var steps = [
       is(event.reason, "create", "Same reason");
       next();
     }
 
     req = navigator.mozContacts.save(createResult1);
     req.onsuccess = function () {
       ok(createResult1.id, "The contact now has an ID.");
       sample_id1 = createResult1.id;
-      checkContacts(properties1, createResult1);
+      checkContacts(createResult1, properties1);
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 1");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       dump("findResult: " + JSON.stringify(findResult1) + "\n");
@@ -381,17 +385,17 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring and update");
     mozContacts.oncontactchange = function(event) {
        is(event.contactID, findResult1.id, "Same contactID");
        is(event.reason, "update", "Same reason");
      }
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       findResult1.jobTitle = ["new Job"];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
@@ -412,17 +416,17 @@ var steps = [
       ok(createResult2.id, "The contact now has an ID.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 2");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       checkContacts(createResult1, findResult1);
       next();
     };
@@ -438,17 +442,17 @@ var steps = [
     req.onsuccess = function () {
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 3");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 0, "Found no contact.");
       next();
     };
     req.onerror = onFailure;
   },
@@ -462,17 +466,17 @@ var steps = [
     req.onsuccess = function () {
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 4");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 0, "Found no contact.");
       next();
     };
     req.onerror = onFailure;
   },
@@ -493,26 +497,26 @@ var steps = [
     ok(true, "Adding a new contact with properties1");
     createResult1 = new mozContact();
     createResult1.init(properties1);
     mozContacts.oncontactchange = null;
     req = navigator.mozContacts.save(createResult1);
     req.onsuccess = function () {
       ok(createResult1.id, "The contact now has an ID.");
       sample_id1 = createResult1.id;
-      checkContacts(properties1, createResult1);
+      checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring tel1");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
-                   filterValue: properties1.tel[1].value.substring(0,5)};
+                   filterValue: properties1.tel[1].value.substring(2,5)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
@@ -627,77 +631,77 @@ var steps = [
     req.onerror = function() {
       ok(true, "Failed");
       next();
     }
   },
   function () {
     ok(true, "Retrieving by substring tel2");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: "9876"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring tel3");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: "98763456"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 5");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring 6");
     var options = {filterBy: ["familyName", "givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring3, Testing multi entry");
     var options = {filterBy: ["givenName", "familyName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.familyName[1].substring(0,3).toLowerCase()};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
@@ -788,32 +792,32 @@ var steps = [
       }
       req2.onerror = onFailure;
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching contacts by query");
     var options = {filterBy: ["givenName", "email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0,4)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(findResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching contacts by query");
     var options = {filterBy: ["givenName", "email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0]};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(findResult1, properties1);
       next();
@@ -839,17 +843,17 @@ var steps = [
     ok(true, "Modifying contact3");
     findResult1.email = [{value: properties1.nickname}];
     findResult1.nickname = "TEST";
     var newContact = new mozContact();
     newContact.init(findResult1);
     req = mozContacts.save(newContact);
     req.onsuccess = function () {
       var options = {filterBy: ["email", "givenName"],
-                     filterOp: "contains",
+                     filterOp: "startsWith",
                      filterValue: properties1.givenName[0]};
       // One contact has it in nickname and the other in email
       var req2 = mozContacts.find(options);
       req2.onsuccess = function () {
         is(req2.result.length, 2, "Found exactly 2 contacts.");
         ok(req2.result[0].id != req2.result[1].id, "Different ID");
         next();
       }
@@ -911,62 +915,62 @@ var steps = [
       is(req.result.length, 2, "Found exactly 2 contact.");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     console.log("Searching contacts by query1");
     var options = {filterBy: ["givenName", "email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0, 4)}
     req = mozContacts.find(options)
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(findResult1, createResult1);
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching contacts by query2");
     var options = {filterBy: ["givenName", "email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties2.givenName[0].substring(0, 4)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       is(findResult1.adr.length, 2, "Adr length 2");
       checkContacts(findResult1, createResult2);
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching contacts by tel");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
-                   filterValue: properties2.tel[0].value.substring(0, 7)};
+                   filterValue: properties2.tel[0].value.substring(3, 7)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id2, "Same ID");
       checkContacts(findResult1, createResult2);
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching contacts by email");
     var options = {filterBy: ["email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties2.email[0].value.substring(0, 4)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id2, "Same ID");
       checkContacts(findResult1, createResult2);
       next();
@@ -994,17 +998,17 @@ var steps = [
       req.onerror = onFailure;
     };
     createResult1 = new mozContact();
     createResult1.init(properties1);
     req = mozContacts.save(createResult1);
     req.onsuccess = function () {
       ok(createResult1.id, "The contact now has an ID.");
       ok(createResult1.name == properties1.name, "Same Name");
-      next();
+      checkCount(20, "20 contacts in DB", next);
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
       is(req.result.length, 20, "20 Entries.");
@@ -1032,30 +1036,30 @@ var steps = [
       is(req.result.length, 10, "10 Entries.");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts2");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0, 4)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 20, "20 Entries.");
       checkContacts(createResult1, req.result[19]);
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts3");
     var options = {filterBy: ["givenName", "tel", "email"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName[0].substring(0, 4),
                    filterLimit: 15 };
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 15, "15 Entries.");
       checkContacts(createResult1, req.result[10]);
       next();
     }
@@ -1086,27 +1090,27 @@ var steps = [
     ok(true, "Testing clone contact2");
     var cloned = new mozContact(createResult1);
     ok(cloned.id != createResult1.id, "Cloned contact has new ID");
     cloned.email = {value: "new email!"};
     cloned.givenName = "Tom";
     req = mozContacts.save(cloned);
     req.onsuccess = function () {
       ok(cloned.id, "The contact now has an ID.");
-      ok(cloned.email.value == "new email!", "Same Email");
+      ok(cloned.email[0].value == "new email!", "Same Email");
       ok(createResult1.email != cloned.email, "Clone has different email");
       ok(cloned.givenName == "Tom", "New Name");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties2.givenName[0].substring(0, 4)};
     req = mozContacts.find({});
     req.onsuccess = function () {
       is(req.result.length, 2, "2 Entries.");
       next();
     }
     req.onerror = onFailure;
   },
@@ -1250,17 +1254,17 @@ var steps = [
     req = mozContacts.save(createResult1);
     req.onsuccess = function() {
       var options = {filterBy: [],
                      filterOp: "equals",
                      filterValue: "customTest"};
       var req2 = mozContacts.find(options);
       req2.onsuccess = function() {
         is(req2.result.length, 1, "1 Entry");
-        checkStr(req2.result.givenName, "customTest", "same name");
+        checkStrArray(req2.result.givenName, "customTest", "same name");
         ok(req2.result.yyy === undefined, "custom property undefined");
         next();
       }
       req2.onerror = onFailure;
     }
     req.onerror = onFailure;
   },
   function () {
@@ -1280,19 +1284,19 @@ var steps = [
     req.onsuccess = function () {
       ok(createResult2.id, "The contact now has an ID.");
       sample_id2 = createResult2.id;
       next();
     };
     req.onerror = onFailure;
   },
   function () {
-    ok(true, "Test category search with contains");
+    ok(true, "Test category search with startsWith");
     var options = {filterBy: ["category"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties2.category[0]};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "1 Entry.");
       checkContacts(req.result[0], createResult2);
       next();
     }
     req.onerror = onFailure;
@@ -1329,17 +1333,17 @@ var steps = [
       sample_id1 = createResult1.id;
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Test category search with equals");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: "5"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       is(req.result.length, 1, "1 Entry.");
       checkContacts(req.result[0], createResult1);
       next();
     }
     req.onerror = onFailure;
@@ -1425,36 +1429,53 @@ var steps = [
     ok(true, "Deleting database");
     req = mozContacts.clear()
     req.onsuccess = function () {
       ok(true, "Deleted the database");
       next();
     }
     req.onerror = onFailure;
   },
+  function() {
+    ok(true, "Test setting array properties to scalar values")
+    const DOMStrings = ["name","honorificPrefix","givenName","additionalName",
+                        "familyName", "honorificSuffix","nickname","category",
+                        "org","jobTitle","note"];
+    const FIELDS = ["email","url","adr","tel","impp"];
+    createResult1 = new mozContact();
+    for (var prop of DOMStrings) {
+      createResult1[prop] = "foo";
+      ok(Array.isArray(createResult1[prop]), prop + " is array");
+    }
+    for (var prop of FIELDS) {
+      createResult1[prop] = {type: "foo"};
+      ok(Array.isArray(createResult1[prop]), prop + " is array");
+    }
+    next();
+  },
   function () {
     ok(true, "all done!\n");
     clearTemps();
 
     SimpleTest.finish();
   }
 ];
 
 function next() {
   ok(true, "Begin!");
   if (index >= steps.length) {
     ok(false, "Shouldn't get here!");
     return;
   }
   try {
-    steps[index]();
+    var i = index++;
+    steps[i]();
   } catch(ex) {
     ok(false, "Caught exception", ex);
   }
-  index += 1;
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(next);
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/contacts/tests/test_contacts_blobs.html
+++ b/dom/contacts/tests/test_contacts_blobs.html
@@ -177,17 +177,17 @@ var steps = [
       sample_id1 = createResult1.id;
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties1.givenName.substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       verifyBlobArray(createResult1.photo, properties1.photo);
     };
@@ -203,17 +203,17 @@ var steps = [
       sample_id1 = createResult1.id;
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: properties2.givenName.substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       verifyBlobArray(createResult1.photo, properties2.photo);
     };
@@ -242,17 +242,17 @@ var steps = [
       is(createResult1.photo, null, "No photo")
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: "asdf"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       is(findResult1.photo, null, "No photo");
       next();
@@ -301,9 +301,9 @@ function next() {
   index += 1;
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(next);
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/contacts/tests/test_contacts_international.html
+++ b/dom/contacts/tests/test_contacts_international.html
@@ -94,31 +94,31 @@ var steps = [
       ok(createResult2.id, "The contact now has an ID.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number1.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for international number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number1.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 0, "Found exactly 0 contacts.");
       next();
     };
     req.onerror = onFailure;
   },
@@ -166,55 +166,55 @@ var steps = [
     req = mozContacts.save(findResult1);
     req.onsuccess = function () {
       next();
     };
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number1.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 0, "Found exactly 0 contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number1.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 0, "Found exactly 0 contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number2.local};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Searching for local number");
     var options = {filterBy: ["tel"],
-                   filterOp: "contains",
+                   filterOp: "startsWith",
                    filterValue: number2.international};
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 0, "Found exactly 1 contact.");
       next();
     };
     req.onerror = onFailure;
   },
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -106,57 +106,42 @@ DeviceStorageUsedSpaceCache::GetCacheEnt
     RefPtr<CacheEntry> cacheEntry = mCacheEntries[i];
     if (cacheEntry->mStorageName.Equals(aStorageName)) {
       return cacheEntry;
     }
   }
   return nullptr;
 }
 
+static int64_t
+GetFreeBytes(const nsAString& aStorageName)
+{
+  // This function makes the assumption that the various types
+  // are all stored on the same filesystem. So we use pictures.
+
+  DeviceStorageFile dsf(NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), aStorageName);
+  int64_t freeBytes = 0;
+  dsf.GetDiskFreeSpace(&freeBytes);
+  return freeBytes;
+}
+
 nsresult
-DeviceStorageUsedSpaceCache::GetUsedSizeForType(const nsAString& aStorageType,
-                                                const nsAString& aStorageName,
-                                                uint64_t* usedSize)
+DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
+                                            uint64_t* aPicturesSoFar,
+                                            uint64_t* aVideosSoFar,
+                                            uint64_t* aMusicSoFar,
+                                            uint64_t* aTotalSoFar)
 {
   RefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   if (!cacheEntry || cacheEntry->mDirty) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-
-  if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
-    *usedSize = cacheEntry->mPicturesUsedSize;
-    return NS_OK;
-  }
-
-  if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
-    *usedSize = cacheEntry->mVideosUsedSize;
-    return NS_OK;
-  }
-
-  if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
-    *usedSize = cacheEntry->mMusicUsedSize;
-    return NS_OK;
-  }
-
-  if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
-    *usedSize = cacheEntry->mTotalUsedSize;
-    return NS_OK;
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-nsresult DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
-                                                     uint64_t* aPicturesSoFar,
-                                                     uint64_t* aVideosSoFar,
-                                                     uint64_t* aMusicSoFar,
-                                                     uint64_t* aTotalSoFar)
-{
-  RefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
-  if (!cacheEntry || cacheEntry->mDirty) {
+  int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName);
+  if (freeBytes != cacheEntry->mFreeBytes) {
+    // Free space changed, so our cached results are no longer valid.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *aPicturesSoFar += cacheEntry->mPicturesUsedSize;
   *aVideosSoFar += cacheEntry->mVideosUsedSize;
   *aMusicSoFar += cacheEntry->mMusicUsedSize;
   *aTotalSoFar += cacheEntry->mTotalUsedSize;
 
@@ -171,16 +156,17 @@ DeviceStorageUsedSpaceCache::SetUsedSize
                                           uint64_t aTotalUsedSize)
 {
   RefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   if (!cacheEntry) {
     cacheEntry = new CacheEntry;
     cacheEntry->mStorageName = aStorageName;
     mCacheEntries.AppendElement(cacheEntry);
   }
+  cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName);
 
   cacheEntry->mPicturesUsedSize = aPictureSize;
   cacheEntry->mVideosUsedSize = aVideosSize;
   cacheEntry->mMusicUsedSize = aMusicSize;
   cacheEntry->mTotalUsedSize = aTotalUsedSize;
   cacheEntry->mDirty = false;
 }
 
@@ -3053,16 +3039,24 @@ nsDOMDeviceStorage::GetRootDirectoryForF
   }
   if (!ds || !ds->mRootDirectory) {
     return NS_ERROR_FAILURE;
   }
   return ds->mRootDirectory->Clone(aRootDirectory);
 }
 
 NS_IMETHODIMP
+nsDOMDeviceStorage::GetDefault(bool* aDefault) {
+  nsString defaultStorageName;
+  GetWritableStorageName(mStorageType, defaultStorageName);
+  *aDefault = mStorageName.Equals(defaultStorageName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName)
 {
   aStorageName = mStorageName;
   return NS_OK;
 }
 
 already_AddRefed<DOMCursor>
 nsDOMDeviceStorage::Enumerate(const nsAString& aPath,
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -98,35 +98,33 @@ public:
   void Dispatch(nsIRunnable* aRunnable)
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     NS_ASSERTION(mIOThread, "Null mIOThread!");
 
     mIOThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
   }
 
-  nsresult GetUsedSizeForType(const nsAString& aStorageType,
-                              const nsAString& aStorageName,
-                              uint64_t* usedSize);
   nsresult AccumUsedSizes(const nsAString& aStorageName,
                           uint64_t* aPictureSize, uint64_t* aVideosSize,
                           uint64_t* aMusicSize, uint64_t* aTotalSize);
 
   void SetUsedSizes(const nsAString& aStorageName,
                     uint64_t aPictureSize, uint64_t aVideosSize,
                     uint64_t aMusicSize, uint64_t aTotalSize);
 
 private:
   friend class InvalidateRunnable;
 
   class CacheEntry : public mozilla::RefCounted<CacheEntry> 
   {
   public:
     bool mDirty;
     nsString mStorageName;
+    int64_t  mFreeBytes;
     uint64_t mPicturesUsedSize;
     uint64_t mVideosUsedSize;
     uint64_t mMusicUsedSize;
     uint64_t mTotalUsedSize;
   };
   mozilla::TemporaryRef<CacheEntry> GetCacheEntry(const nsAString& aStorageName);
 
   nsTArray<mozilla::RefPtr<CacheEntry> > mCacheEntries;
--- a/dom/indexedDB/FileManager.cpp
+++ b/dom/indexedDB/FileManager.cpp
@@ -380,22 +380,33 @@ FileManager::InitDirectory(nsIFile* aDir
 
   return NS_OK;
 }
 
 // static
 nsresult
 FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
 {
-  uint64_t usage = 0;
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  bool exists;
+  nsresult rv = aDirectory->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!exists) {
+    *aUsage = 0;
+    return NS_OK;
+  }
 
   nsCOMPtr<nsISimpleEnumerator> entries;
-  nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  uint64_t usage = 0;
+
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
     NS_ENSURE_TRUE(file, NS_NOINTERFACE);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -37,17 +37,17 @@ interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 
-[scriptable, uuid(dba80826-6251-4947-bf2a-a3873b9ec764)]
+[scriptable, uuid(a806d366-cc52-11e2-bc9a-ba3212c84021)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -654,16 +654,21 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Returns the scrollbar width of the window's scroll frame.
    *
    * @param aFlushLayout flushes layout if true. Otherwise, no flush occurs.
    */
   void getScrollbarSize(in boolean aFlushLayout, out long aWidth, out long aHeight);
 
   /**
+   * Returns the given element's bounds without flushing pending layout changes.
+   */
+  nsIDOMClientRect getBoundsWithoutFlushing(in nsIDOMElement aElement);
+
+  /**
    * Returns the bounds of the window's currently loaded document. This will
    * generally be (0, 0, pageWidth, pageHeight) but in some cases (e.g. RTL
    * documents) may have a negative left value.
    */
   nsIDOMClientRect getRootBounds();
 
   /**
    * Get IME open state. TRUE means 'Open', otherwise, 'Close'.
--- a/dom/interfaces/contacts/nsIContactProperties.idl
+++ b/dom/interfaces/contacts/nsIContactProperties.idl
@@ -36,17 +36,17 @@ interface nsIContactFindSortOptions : ns
   attribute DOMString sortBy;       // "givenName" or "familyName"
   attribute DOMString sortOrder;    // e.g. "descending"
 };
 
 [scriptable, uuid(28ce07d0-45d9-4b7a-8843-521df4edd8bc)]
 interface nsIContactFindOptions : nsIContactFindSortOptions
 {
   attribute DOMString filterValue;  // e.g. "Tom"
-  attribute DOMString filterOp;     // e.g. "contains"
+  attribute DOMString filterOp;     // e.g. "startsWith"
   attribute jsval filterBy;         // DOMString[], e.g. ["givenName", "nickname"]
   attribute unsigned long filterLimit;
 };
 
 [scriptable, uuid(6cb78b21-4218-414b-8a84-3b7bf0088b34)]
 interface nsIContactProperties : nsISupports
 {
   attribute jsval         name;               // DOMString[]
--- a/dom/interfaces/contacts/nsIDOMContactManager.idl
+++ b/dom/interfaces/contacts/nsIDOMContactManager.idl
@@ -15,25 +15,27 @@ interface nsIDOMContact : nsIContactProp
 {
   attribute DOMString id;
   readonly attribute jsval     published;
   readonly attribute jsval     updated;
 
   void init(in nsIContactProperties properties);  // Workaround BUG 723206
 };
 
-[scriptable, uuid(e01ebfe7-e972-4e01-b04b-1d162dc74983)]
+[scriptable, uuid(8beb3a66-d70a-4111-b216-b8e995ad3aff)]
 interface nsIDOMContactManager : nsISupports
 {
   nsIDOMDOMRequest find(in nsIContactFindOptions options);
 
   nsIDOMDOMCursor getAll(in nsIContactFindSortOptions options);
 
   nsIDOMDOMRequest clear();
 
   nsIDOMDOMRequest save(in nsIDOMContact contact);
 
   nsIDOMDOMRequest remove(in nsIDOMContact contact);
 
   attribute nsIDOMEventListener oncontactchange;
 
   nsIDOMDOMRequest getRevision();
+
+  nsIDOMDOMRequest getCount();
 };
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -6,17 +6,17 @@
 #include "nsIDOMEventTarget.idl"
 interface nsIDOMBlob;
 interface nsIDOMDOMRequest;
 interface nsIDOMDOMCursor;
 interface nsIDOMDeviceStorageChangeEvent;
 interface nsIDOMEventListener;
 interface nsIFile;
 
-[scriptable, uuid(7cef14d4-d767-4da7-a18e-32c5e009a8e4), builtinclass]
+[scriptable, uuid(7c1b2305-0f14-4c07-8a8a-359eeb850068), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     nsIDOMDOMRequest get([Null(Stringify)] in DOMString aName);
     nsIDOMDOMRequest getEditable([Null(Stringify)] in DOMString aName);
@@ -25,10 +25,14 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     nsIDOMDOMRequest freeSpace();
     nsIDOMDOMRequest usedSpace();
     nsIDOMDOMRequest available();
 
     // Note that the storageName is just a name (like sdcard), and doesn't
     // include any path information.
     readonly attribute DOMString storageName;
 
+    // Determines if this storage area is the one which will be used by default
+    // for storing new files.
+    readonly attribute bool default;
+
     [noscript] nsIFile getRootDirectoryForFile(in DOMString aName);
 };
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
@@ -11,12 +11,12 @@
  * <audio> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#audio
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(2d6580a3-bb24-4f13-8c49-09e332a50049)]
+[scriptable, uuid(fdfda110-e96b-4e98-8716-167a6555c80a)]
 interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
 {
 };
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -22,17 +22,17 @@ interface nsIDOMMediaStream;
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[scriptable, uuid(adbb2541-5ab6-41a3-9e16-fca46620b532)]
+[scriptable, uuid(21354ac9-7166-46a0-a3f0-a3135c3cc804)]
 interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
            attribute nsIDOMMediaStream mozSrcObject;
@@ -95,24 +95,16 @@ interface nsIDOMHTMLMediaElement : nsIDO
            attribute unsigned long mozFrameBufferLength;
 
   // Mozilla extension: return embedded metadata from the stream as a
   // JSObject with key:value pairs for each tag. This can be used by
   // player interfaces to display the song title, artist, etc.
   [implicit_jscontext]
   jsval mozGetMetadata();
 
-  // Mozilla extension: load data from another media element. This is like
-  // load() but we don't run the resource selection algorithm; instead
-  // we just set our source to other's currentSrc. This is optimized
-  // so that this element will get access to all of other's cached/
-  // buffered data. In fact any future data downloaded by this element or
-  // other will be sharable by both elements.
-  void mozLoadFrom(in nsIDOMHTMLMediaElement other);
-
   // Mozilla extension: provides access to the fragment end time if
   // the media element has a fragment URI for the currentSrc, otherwise
   // it is equal to the media duration.
   readonly attribute double mozFragmentEnd;
 
    // Mozilla extension: an audio channel type for media elements.
    // An exception is thrown if the app tries to change the audio channel type
    // without the permission (manifest file for B2G apps).
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
@@ -11,17 +11,17 @@
  * <video> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#video
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(df152de2-9829-444a-b92b-83985b0978bd)]
+[scriptable, uuid(b48ec2c0-7529-4212-9717-1ce95507e7e4)]
 interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
 {
            attribute long width; 
            attribute long height;
   readonly attribute unsigned long videoWidth;
   readonly attribute unsigned long videoHeight;
            attribute DOMString poster;
            
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -945,22 +945,27 @@ ContentChild::ActorDestroy(ActorDestroyR
 void
 ContentChild::ProcessingError(Result what)
 {
     switch (what) {
     case MsgDropped:
         QuickExit();
 
     case MsgNotKnown:
+        NS_RUNTIMEABORT("aborting because of MsgNotKnown");
     case MsgNotAllowed:
+        NS_RUNTIMEABORT("aborting because of MsgNotAllowed");
     case MsgPayloadError:
+        NS_RUNTIMEABORT("aborting because of MsgPayloadError");
     case MsgProcessingError:
+        NS_RUNTIMEABORT("aborting because of MsgProcessingError");
     case MsgRouteError:
+        NS_RUNTIMEABORT("aborting because of MsgRouteError");
     case MsgValueError:
-        NS_RUNTIMEABORT("aborting because of fatal error");
+        NS_RUNTIMEABORT("aborting because of MsgValueError");
 
     default:
         NS_RUNTIMEABORT("not reached");
     }
 }
 
 void
 ContentChild::QuickExit()
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -345,18 +345,19 @@ TabChild::Observe(nsISupports *aSubject,
 
         // Calculate a really simple resolution that we probably won't
         // be keeping, as well as putting the scroll offset back to
         // the top-left of the page.
         mLastMetrics.mZoom = gfxSize(1.0, 1.0);
         mLastMetrics.mViewport =
             gfx::Rect(0, 0,
                       kDefaultViewportSize.width, kDefaultViewportSize.height);
-        mLastMetrics.mCompositionBounds = nsIntRect(nsIntPoint(0, 0),
-                                                    mInnerSize);
+        // I don't know what units mInnerSize is in, hence FromUnknownRect
+        mLastMetrics.mCompositionBounds = LayerIntRect::FromUnknownRect(
+          gfx::IntRect(0, 0, mInnerSize.width, mInnerSize.height));
         mLastMetrics.mResolution =
           AsyncPanZoomController::CalculateResolution(mLastMetrics);
         mLastMetrics.mScrollOffset = CSSPoint(0, 0);
         utils->SetResolution(mLastMetrics.mResolution.width,
                              mLastMetrics.mResolution.height);
 
         HandlePossibleViewportChange();
       }
@@ -585,18 +586,20 @@ TabChild::HandlePossibleViewportChange()
   // respect to CSS pixels because of the CSS viewport size changing.
   int32_t oldScreenWidth = mLastMetrics.mCompositionBounds.width;
   if (!oldScreenWidth) {
     oldScreenWidth = mInnerSize.width;
   }
 
   FrameMetrics metrics(mLastMetrics);
   metrics.mViewport = gfx::Rect(0.0f, 0.0f, viewportW, viewportH);
-  metrics.mScrollableRect = gfx::Rect(0.0f, 0.0f, pageWidth, pageHeight);
-  metrics.mCompositionBounds = nsIntRect(0, 0, mInnerSize.width, mInnerSize.height);
+  metrics.mScrollableRect = CSSRect(0.0f, 0.0f, pageWidth, pageHeight);
+  // I don't know what units mInnerSize is in, hence FromUnknownRect
+  metrics.mCompositionBounds = LayerIntRect::FromUnknownRect(
+    gfx::IntRect(0, 0, mInnerSize.width, mInnerSize.height));
 
   // Changing the zoom when we're not doing a first paint will get ignored
   // by AsyncPanZoomController and causes a blurry flash.
   bool isFirstPaint;
   nsresult rv = utils->GetIsFirstPaint(&isFirstPaint);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   if (NS_FAILED(rv) || isFirstPaint) {
     gfxSize intrinsicScale =
@@ -1478,17 +1481,17 @@ TabChild::RecvUpdateFrame(const FrameMet
 
 bool
 TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
   {
     if (!mCx || !mTabChildGlobal) {
         return true;
     }
 
-    gfx::Rect cssCompositedRect =
+    CSSRect cssCompositedRect =
       AsyncPanZoomController::CalculateCompositedRectInCssPixels(aFrameMetrics);
     // The BrowserElementScrolling helper must know about these updated metrics
     // for other functions it performs, such as double tap handling.
     nsCString data;
     data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
     data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
     data += nsPrintfCString(", \"viewport\" : ");
         data += nsPrintfCString("{ \"width\" : %f", aFrameMetrics.mViewport.width);
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -807,16 +807,33 @@ RTCPeerConnection.prototype = {
     return state;
   },
 
   createDataChannel: function(label, dict) {
     this._checkClosed();
     if (dict == undefined) {
       dict = {};
     }
+    if (dict.maxRetransmitNum != undefined) {
+      dict.maxRetransmits = dict.maxRetransmitNum;
+      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry maxRetransmitNum used!", null, 0);
+    }
+    if (dict.outOfOrderAllowed != undefined) {
+      dict.ordered = !dict.outOfOrderAllowed; // the meaning is swapped with the name change
+      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry outOfOrderAllowed used!", null, 0);
+    }
+    if (dict.preset != undefined) {
+      dict.negotiated = dict.preset;
+      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry preset used!", null, 0);
+    }
+    if (dict.stream != undefined) {
+      dict.id = dict.stream;
+      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry stream used!", null, 0);
+    }
+
     if (dict.maxRetransmitTime != undefined &&
         dict.maxRetransmitNum != undefined) {
       throw new Components.Exception("Both maxRetransmitTime and maxRetransmitNum cannot be provided");
     }
     let protocol;
     if (dict.protocol == undefined) {
       protocol = "";
     } else {
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -378,20 +378,22 @@ SystemMessageInternal.prototype = {
           pendingMessages.push(aMessage.msg);
         });
 
         // Clear the pending queue for this page. This is OK since we'll store
         // pending messages in the content process (|SystemMessageManager|).
         page.pendingMessages.length = 0;
 
         // Send the array of pending messages.
-        aMessage.target.sendAsyncMessage("SystemMessageManager:GetPendingMessages:Return",
-                                         { type: msg.type,
-                                           manifest: msg.manifest,
-                                           msgQueue: pendingMessages });
+        aMessage.target
+                .sendAsyncMessage("SystemMessageManager:GetPendingMessages:Return",
+                                  { type: msg.type,
+                                    manifest: msg.manifest,
+                                    uri: msg.uri,
+                                    msgQueue: pendingMessages });
         break;
       }
       case "SystemMessageManager:HasPendingMessages":
       {
         debug("received SystemMessageManager:HasPendingMessages " + msg.type +
           " for " + msg.uri + " @ " + msg.manifest);
 
         // This is a sync call used to return if a page has pending messages.
@@ -538,33 +540,39 @@ SystemMessageInternal.prototype = {
     let pageKey = this._createKeyForPage({ type: aType,
                                            manifest: aManifestURI,
                                            uri: aPageURI })
 
     let targets = this._listeners[aManifestURI];
     if (targets) {
       for (let index = 0; index < targets.length; ++index) {
         let target = targets[index];
-        // We only need to send the system message to the targets which match
-        // the manifest URL and page URL of the destination of system message.
+        // We only need to send the system message to the targets (processes)
+        // which contain the window page that matches the manifest/page URL of
+        // the destination of system message.
         if (target.winCounts[aPageURI] === undefined) {
           continue;
         }
 
         appPageIsRunning = true;
         // We need to acquire a CPU wake lock for that page and expect that
         // we'll receive a "SystemMessageManager:HandleMessagesDone" message
         // when the page finishes handling the system message. At that point,
         // we'll release the lock we acquired.
         this._acquireCpuWakeLock(pageKey);
 
+        // Multiple windows can share the same target (process), the content
+        // window needs to check if the manifest/page URL is matched. Only
+        // *one* window should handle the system message.
         let manager = target.target;
         manager.sendAsyncMessage("SystemMessageManager:Message",
                                  { type: aType,
                                    msg: aMessage,
+                                   manifest: aManifestURI,
+                                   uri: aPageURI,
                                    msgID: aMessageID });
       }
     }
 
     if (!appPageIsRunning) {
       // The app page isn't running and relies on the 'open-app' chrome event to
       // wake it up. We still need to acquire a CPU wake lock for that page and
       // expect that we will receive a "SystemMessageManager:HandleMessagesDone"
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -154,20 +154,28 @@ SystemMessageManager.prototype = {
   //     This one will only be received when the child process is alive when
   //     the message is initially sent.
   //
   //   - SystemMessageManager:GetPendingMessages:Return
   //     This one will be received when the starting child process wants to
   //     retrieve the pending system messages from the parent (i.e. after
   //     sending SystemMessageManager:GetPendingMessages).
   receiveMessage: function sysMessMgr_receiveMessage(aMessage) {
-    debug("receiveMessage " + aMessage.name + " for [" + aMessage.data.type + "] " +
-          "with manifest = " + this._manifest + " and uri = " + this._uri);
+    let msg = aMessage.data;
+    debug("receiveMessage " + aMessage.name + " for [" + msg.type + "] " +
+          "with manifest = " + msg.manifest + " and uri = " + msg.uri);
 
-    let msg = aMessage.data;
+    // Multiple windows can share the same target (process), the content
+    // window needs to check if the manifest/page URL is matched. Only
+    // *one* window should handle the system message.
+    if (msg.manifest !== this._manifest || msg.uri !== this._uri) {
+      debug("This page shouldn't handle the messages because its " +
+            "manifest = " + this._manifest + " and uri = " + this._uri);
+      return;
+    }
 
     if (aMessage.name == "SystemMessageManager:Message") {
       // Send an acknowledgement to parent to clean up the pending message,
       // so a re-launched app won't handle it again, which is redundant.
       cpmm.sendAsyncMessage("SystemMessageManager:Message:Return:OK",
                             { type: msg.type,
                               manifest: this._manifest,
                               uri: this._uri,
--- a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
+++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
@@ -6,17 +6,17 @@
 
 interface nsIDOMEventListener;
 interface nsIDOMMozSmsFilter;
 interface nsIDOMMozSmsSegmentInfo;
 interface nsIDOMDOMCursor;
 interface nsIDOMDOMRequest;
 interface nsIDOMBlob;
 
-[scriptable, builtinclass, uuid(a7984cb3-27c8-4e3d-82a4-01553e93c078)]
+[scriptable, builtinclass, uuid(efff5276-0f3f-4137-9b16-66e894400e01)]
 interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
 {
   nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
 
   // The first parameter can be either a DOMString (only one number) or an array
   // of DOMStrings.
   // The method returns a DOMRequest object if one number has been passed.
   // An array of DOMRequest objects otherwise.
@@ -36,14 +36,15 @@ interface nsIDOMMozMobileMessageManager 
   nsIDOMDOMRequest markMessageRead(in long id, in boolean aValue);
 
   // Iterates through nsIDOMMozMobileMessageThread.
   nsIDOMDOMCursor getThreads();
 
   nsIDOMDOMRequest retrieveMMS(in long id);
 
   [implicit_jscontext] attribute jsval onreceived;
+  [implicit_jscontext] attribute jsval onretrieving;
   [implicit_jscontext] attribute jsval onsending;
   [implicit_jscontext] attribute jsval onsent;
   [implicit_jscontext] attribute jsval onfailed;
   [implicit_jscontext] attribute jsval ondeliverysuccess;
   [implicit_jscontext] attribute jsval ondeliveryerror;
 };
--- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
+++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
@@ -10,23 +10,24 @@
 interface nsIRilMobileMessageDatabaseCallback : nsISupports
 {
   /**
    * |aDomMessage|: the nsIDOMMoz{Mms,Sms}Message
    */
   void notify(in nsresult aRv, in nsISupports aDomMessage);
 };
 
-[scriptable, function, uuid(2be52603-5885-412c-9225-f78a78f1bbbd)]
+[scriptable, function, uuid(32b02bbe-60a1-45e0-a748-ad40709b09dd)]
 interface nsIRilMobileMessageDatabaseRecordCallback : nsISupports
 {
   /**
    * |aMessageRecord| Object: the mobile-message database record
+   * |aDomMessage|: the nsIDOMMoz{Mms,Sms}Message. Noted, this value might be null.
    */
-  void notify(in nsresult aRv, in jsval aMessageRecord);
+  void notify(in nsresult aRv, in jsval aMessageRecord, in nsISupports aDomMessage);
 };
 
 [scriptable, uuid(0ead3154-542d-4e2c-a624-9e3cec504758)]
 interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
 {
   /**
    * |aMessage| Object: should contain the following properties for internal use:
    *   - |type| DOMString: "sms" or "mms"
@@ -74,17 +75,17 @@ interface nsIRilMobileMessageDatabaseSer
                           in DOMString aReceiver,
                           in DOMString aDelivery,
                           in DOMString aDeliveryStatus,
                [optional] in nsIRilMobileMessageDatabaseCallback aCallback);
 
   /**
    * |aMessageId| Number: the message's DB record ID.
    * |aCallback| nsIRilMobileMessageDatabaseCallback: a callback which takes
-   *   result flag and message record as parameters.
+   *   result flag, message record and domMessage as parameters.
    */
   void getMessageRecordById(in long aMessageId,
                             in nsIRilMobileMessageDatabaseRecordCallback aCallback);
 
   /**
    * |aTransactionId| DOMString: the transaction ID of MMS pdu.
    * |aCallback| nsIRilMobileMessageDatabaseCallback: a callback which takes
    *   result flag and message record as parameters.
--- a/dom/mobilemessage/src/Constants.cpp
+++ b/dom/mobilemessage/src/Constants.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 const char* kSmsReceivedObserverTopic        = "sms-received";
+const char* kSmsRetrievingObserverTopic      = "sms-retrieving";
 const char* kSmsSendingObserverTopic         = "sms-sending";
 const char* kSmsSentObserverTopic            = "sms-sent";
 const char* kSmsFailedObserverTopic          = "sms-failed";
 const char* kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
 const char* kSmsDeliveryErrorObserverTopic   = "sms-delivery-error";
 
 } // namespace mobilemessage
 } // namespace dom
--- a/dom/mobilemessage/src/Constants.h
+++ b/dom/mobilemessage/src/Constants.h
@@ -7,16 +7,17 @@
 #define mozilla_dom_mobilemessage_Constants_h
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 // Defined in the .cpp.
 extern const char* kSmsReceivedObserverTopic;
+extern const char* kSmsRetrievingObserverTopic;
 extern const char* kSmsSendingObserverTopic;
 extern const char* kSmsSentObserverTopic;
 extern const char* kSmsFailedObserverTopic;
 extern const char* kSmsDeliverySuccessObserverTopic;
 extern const char* kSmsDeliveryErrorObserverTopic;
 
 extern const char* kMmsSendingObserverTopic;
 extern const char* kMmsSentObserverTopic;
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -25,16 +25,17 @@
 #include "GeneratedEvents.h"
 #include "DOMRequest.h"
 #include "nsIMobileMessageCallback.h"
 #include "MobileMessageCallback.h"
 #include "MobileMessageCursorCallback.h"
 #include "DOMCursor.h"
 
 #define RECEIVED_EVENT_NAME         NS_LITERAL_STRING("received")
+#define RETRIEVING_EVENT_NAME       NS_LITERAL_STRING("retrieving")
 #define SENDING_EVENT_NAME          NS_LITERAL_STRING("sending")
 #define SENT_EVENT_NAME             NS_LITERAL_STRING("sent")
 #define FAILED_EVENT_NAME           NS_LITERAL_STRING("failed")
 #define DELIVERY_SUCCESS_EVENT_NAME NS_LITERAL_STRING("deliverysuccess")
 #define DELIVERY_ERROR_EVENT_NAME   NS_LITERAL_STRING("deliveryerror")
 
 using namespace mozilla::dom::mobilemessage;
 
@@ -48,16 +49,17 @@ NS_INTERFACE_MAP_BEGIN(MobileMessageMana
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMobileMessageManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MobileMessageManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MobileMessageManager, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, received)
+NS_IMPL_EVENT_HANDLER(MobileMessageManager, retrieving)
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, sending)
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, sent)
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, failed)
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliverysuccess)
 NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliveryerror)
 
 void
 MobileMessageManager::Init(nsPIDOMWindow *aWindow)
@@ -66,16 +68,17 @@ MobileMessageManager::Init(nsPIDOMWindow
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   // GetObserverService() can return null is some situations like shutdown.
   if (!obs) {
     return;
   }
 
   obs->AddObserver(this, kSmsReceivedObserverTopic, false);
+  obs->AddObserver(this, kSmsRetrievingObserverTopic, false);
   obs->AddObserver(this, kSmsSendingObserverTopic, false);
   obs->AddObserver(this, kSmsSentObserverTopic, false);
   obs->AddObserver(this, kSmsFailedObserverTopic, false);
   obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false);
   obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
 }
 
 void
@@ -83,16 +86,17 @@ MobileMessageManager::Shutdown()
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   // GetObserverService() can return null is some situations like shutdown.
   if (!obs) {
     return;
   }
 
   obs->RemoveObserver(this, kSmsReceivedObserverTopic);
+  obs->RemoveObserver(this, kSmsRetrievingObserverTopic);
   obs->RemoveObserver(this, kSmsSendingObserverTopic);
   obs->RemoveObserver(this, kSmsSentObserverTopic);
   obs->RemoveObserver(this, kSmsFailedObserverTopic);
   obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic);
   obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
 }
 
 NS_IMETHODIMP
@@ -428,16 +432,20 @@ MobileMessageManager::DispatchTrustedSms
 NS_IMETHODIMP
 MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic,
                               const PRUnichar* aData)
 {
   if (!strcmp(aTopic, kSmsReceivedObserverTopic)) {
     return DispatchTrustedSmsEventToSelf(aTopic, RECEIVED_EVENT_NAME, aSubject);
   }
 
+  if (!strcmp(aTopic, kSmsRetrievingObserverTopic)) {
+    return DispatchTrustedSmsEventToSelf(aTopic, RETRIEVING_EVENT_NAME, aSubject);
+  }
+
   if (!strcmp(aTopic, kSmsSendingObserverTopic)) {
     return DispatchTrustedSmsEventToSelf(aTopic, SENDING_EVENT_NAME, aSubject);
   }
 
   if (!strcmp(aTopic, kSmsSentObserverTopic)) {
     return DispatchTrustedSmsEventToSelf(aTopic, SENT_EVENT_NAME, aSubject);
   }
 
--- a/dom/mobilemessage/src/ipc/PSms.ipdl
+++ b/dom/mobilemessage/src/ipc/PSms.ipdl
@@ -83,16 +83,18 @@ union IPCMobileMessageCursor
 sync protocol PSms {
     manager PContent;
     manages PSmsRequest;
     manages PMobileMessageCursor;
 
 child:
   NotifyReceivedMessage(MobileMessageData aMessageData);
 
+  NotifyRetrievingMessage(MobileMessageData aMessageData);
+
   NotifySendingMessage(MobileMessageData aMessageData);
 
   NotifySentMessage(MobileMessageData aMessageData);
 
   NotifyFailedMessage(MobileMessageData aMessageData);
 
   NotifyDeliverySuccessMessage(MobileMessageData aMessageData);
 
--- a/dom/mobilemessage/src/ipc/SmsChild.cpp
+++ b/dom/mobilemessage/src/ipc/SmsChild.cpp
@@ -64,16 +64,23 @@ SmsChild::ActorDestroy(ActorDestroyReaso
 bool
 SmsChild::RecvNotifyReceivedMessage(const MobileMessageData& aData)
 {
   NotifyObserversWithMobileMessage(kSmsReceivedObserverTopic, aData);
   return true;
 }
 
 bool
+SmsChild::RecvNotifyRetrievingMessage(const MobileMessageData& aData)
+{
+  NotifyObserversWithMobileMessage(kSmsRetrievingObserverTopic, aData);
+  return true;
+}
+
+bool
 SmsChild::RecvNotifySendingMessage(const MobileMessageData& aData)
 {
   NotifyObserversWithMobileMessage(kSmsSendingObserverTopic, aData);
   return true;
 }
 
 bool
 SmsChild::RecvNotifySentMessage(const MobileMessageData& aData)
--- a/dom/mobilemessage/src/ipc/SmsChild.h
+++ b/dom/mobilemessage/src/ipc/SmsChild.h
@@ -33,16 +33,19 @@ protected:
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyReceivedMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
   virtual bool
+  RecvNotifyRetrievingMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
+
+  virtual bool
   RecvNotifySendingMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifySentMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyFailedMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
--- a/dom/mobilemessage/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -140,32 +140,34 @@ SmsParent::SmsParent()
 {
   MOZ_COUNT_CTOR(SmsParent);
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return;
   }
 
   obs->AddObserver(this, kSmsReceivedObserverTopic, false);
+  obs->AddObserver(this, kSmsRetrievingObserverTopic, false);
   obs->AddObserver(this, kSmsSendingObserverTopic, false);
   obs->AddObserver(this, kSmsSentObserverTopic, false);
   obs->AddObserver(this, kSmsFailedObserverTopic, false);
   obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false);
   obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
 }
 
 void
 SmsParent::ActorDestroy(ActorDestroyReason why)
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return;
   }
 
   obs->RemoveObserver(this, kSmsReceivedObserverTopic);
+  obs->RemoveObserver(this, kSmsRetrievingObserverTopic);
   obs->RemoveObserver(this, kSmsSendingObserverTopic);
   obs->RemoveObserver(this, kSmsSentObserverTopic);
   obs->RemoveObserver(this, kSmsFailedObserverTopic);
   obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic);
   obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
 }
 
 NS_IMETHODIMP
@@ -178,16 +180,27 @@ SmsParent::Observe(nsISupports* aSubject
       NS_ERROR("Got a 'sms-received' topic without a valid message!");
       return NS_OK;
     }
 
     unused << SendNotifyReceivedMessage(msgData);
     return NS_OK;
   }
 
+  if (!strcmp(aTopic, kSmsRetrievingObserverTopic)) {
+    MobileMessageData msgData;
+    if (!GetMobileMessageDataFromMessage(aSubject, msgData)) {
+      NS_ERROR("Got a 'sms-retrieving' topic without a valid message!");
+      return NS_OK;
+    }
+
+    unused << SendNotifyRetrievingMessage(msgData);
+    return NS_OK;
+  }
+
   if (!strcmp(aTopic, kSmsSendingObserverTopic)) {
     MobileMessageData msgData;
     if (!GetMobileMessageDataFromMessage(aSubject, msgData)) {
       NS_ERROR("Got a 'sms-sending' topic without a valid message!");
       return NS_OK;
     }
 
     unused << SendNotifySendingMessage(msgData);
--- a/dom/mobilemessage/src/ril/MmsService.js
+++ b/dom/mobilemessage/src/ril/MmsService.js
@@ -23,16 +23,17 @@ try {
   let debugPref = Services.prefs.getBoolPref("mms.debugging.enabled");
   DEBUG = DEBUG || debugPref;
 } catch (e) {}
 
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsReceivedObserverTopic          = "sms-received";
+const kSmsRetrievingObserverTopic        = "sms-retrieving";
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kXpcomShutdownObserverTopic        = "xpcom-shutdown";
 const kPrefenceChangedObserverTopic      = "nsPref:changed";
 
 // HTTP status codes:
 // @see http://tools.ietf.org/html/rfc2616#page-39
 const HTTP_STATUS_OK = 200;
@@ -622,32 +623,40 @@ NotifyResponseTransaction.prototype = {
  * @param contentLocation
  *        X-Mms-Content-Location of the message.
  */
 function RetrieveTransaction(contentLocation) {
   this.contentLocation = contentLocation;
 }
 RetrieveTransaction.prototype = {
   /**
+   * We need to keep a reference to the timer to assure the timer is fired.
+   */
+  timer: null,
+
+  /**
    * @param callback [optional]
    *        A callback function that takes two arguments: one for X-Mms-Status,
    *        the other for the parsed M-Retrieve.conf message.
    */
   run: function run(callback) {
     this.retryCount = 0;
     let that = this;
     this.retrieve((function retryCallback(mmsStatus, msg) {
       if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
           that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
-        let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        timer.initWithCallback((function (){
-                                 this.retrieve(retryCallback);
-                               }).bind(that),
-                               PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
-                               Ci.nsITimer.TYPE_ONE_SHOT);
+        if (that.timer == null) {
+          that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+        }
+
+        that.timer.initWithCallback((function (){
+                                      this.retrieve(retryCallback);
+                                    }).bind(that),
+                                    PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
+                                    Ci.nsITimer.TYPE_ONE_SHOT);
         that.retryCount++;
         return;
       }
       if (callback) {
         callback(mmsStatus, msg);
       }
     }).bind(this));
   },
@@ -746,16 +755,21 @@ function SendTransaction(msg) {
     msg.headers["content-type"] = contentType;
   }
 
   if (DEBUG) debug("msg: " + JSON.stringify(msg));
 
   this.msg = msg;
 }
 SendTransaction.prototype = {
+  /**
+   * We need to keep a reference to the timer to assure the timer is fired.
+   */
+  timer: null,
+
   istreamComposed: false,
 
   /**
    * @param parts
    *        'parts' property of a parsed MMS message.
    * @param callback [optional]
    *        A callback function that takes zero argument.
    */
@@ -823,22 +837,25 @@ SendTransaction.prototype = {
       return;
     }
 
     this.retryCount = 0;
     let retryCallback = (function (mmsStatus, msg) {
       if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
             MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
           this.retryCount < PREF_SEND_RETRY_COUNT) {
+        if (this.timer == null) {
+          this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+        }
+
         this.retryCount++;
 
-        let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        timer.initWithCallback(this.send.bind(this, retryCallback),
-                               PREF_SEND_RETRY_INTERVAL,
-                               Ci.nsITimer.TYPE_ONE_SHOT);
+        this.timer.initWithCallback(this.send.bind(this, retryCallback),
+                                    PREF_SEND_RETRY_INTERVAL,
+                                    Ci.nsITimer.TYPE_ONE_SHOT);
         return;
       }
 
       callbackIfValid(mmsStatus, msg);
     }).bind(this);
     this.send(retryCallback);
   },
 
@@ -1034,28 +1051,30 @@ MmsService.prototype = {
     }
     if (intermediate.content) {
       savable.content = intermediate.content;
     }
     return savable;
   },
 
   /**
-   * @param contentLocation
+   * @param aContentLocation
    *        X-Mms-Content-Location of the message.
-   * @param callback [optional]
+   * @param aCallback [optional]
    *        A callback function that takes two arguments: one for X-Mms-Status,
    *        the other parsed MMS message.
+   * @param aDomMessage
+   *        The nsIDOMMozMmsMessage object.
    */
-  retrieveMessage: function retrieveMessage(contentLocation, callback) {
-    // TODO: bug 810099 - support onretrieving event
-    // TODO: bug 809832 - support customizable max incoming/outgoing message size.
+  retrieveMessage: function retrieveMessage(aContentLocation, aCallback, aDomMessage) {
+    // Notifying observers an MMS message is retrieving.
+    Services.obs.notifyObservers(aDomMessage, kSmsRetrievingObserverTopic, null);
 
-    let transaction = new RetrieveTransaction(contentLocation);
-    transaction.run(callback);
+    let transaction = new RetrieveTransaction(aContentLocation);
+    transaction.run(aCallback);
   },
 
   /**
    * A helper to broadcast the system message to launch registered apps
    * like Costcontrol, Notification and Message app... etc.
    *
    * @param aName
    *        The system message name.
@@ -1239,17 +1258,18 @@ MmsService.prototype = {
     }
     let url = savableMessage.headers["x-mms-content-location"].uri;
 
     // For RETRIEVAL_MODE_AUTOMATIC or RETRIEVAL_MODE_AUTOMATIC_HOME but not
     // roaming, proceed to retrieve MMS.
     this.retrieveMessage(url,
                          this.retrieveMessageCallback.bind(this,
                                                            wish,
-                                                           savableMessage));
+                                                           savableMessage),
+                         domMessage);
   },
 
   /**
    * Handle incoming M-Notification.ind PDU.
    *
    * @param notification
    *        The parsed MMS message object.
    */
@@ -1451,17 +1471,17 @@ MmsService.prototype = {
         sendTransactionCb(aDomMessage.id, isSentSuccess);
       });
     });
   },
 
   retrieve: function retrieve(aMessageId, aRequest) {
     if (DEBUG) debug("Retrieving message with ID " + aMessageId);
     gMobileMessageDatabaseService.getMessageRecordById(aMessageId,
-        (function notifyResult(aRv, aMessageRecord) {
+        (function notifyResult(aRv, aMessageRecord, aDomMessage) {
       if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR != aRv) {
         if (DEBUG) debug("Function getMessageRecordById() return error.");
         aRequest.notifyGetMessageFailed(aRv);
         return;
       }
       if ("mms" != aMessageRecord.type) {
         if (DEBUG) debug("Type of message record is not 'mms'.");
         aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
@@ -1501,20 +1521,27 @@ MmsService.prototype = {
         }
       }
 
       let url =  aMessageRecord.headers["x-mms-content-location"].uri;
       // For X-Mms-Report-Allowed
       let wish = aMessageRecord.headers["x-mms-delivery-report"];
       let responseNotify = function responseNotify(mmsStatus, retrievedMsg) {
         // If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry,
-        // we should not store it into database.
+        // we should not store it into database and update its delivery
+        // status to 'error'.
         if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) {
           if (DEBUG) debug("RetrieveMessage fail after retry.");
-          aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+          gMobileMessageDatabaseService.setMessageDelivery(aMessageId,
+                                                           null,
+                                                           null,
+                                                           DELIVERY_STATUS_ERROR,
+                                                           function () {
+            aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+          });
           return;
         }
         // In OMA-TS-MMS_ENC-V1_3, Table 5 in page 25. This header field
         // (x-mms-transaction-id) SHALL be present when the MMS Proxy relay
         // seeks an acknowledgement for the MM delivered though M-Retrieve.conf
         // PDU during deferred retrieval. This transaction ID is used by the MMS
         // Client and MMS Proxy-Relay to provide linkage between the originated
         // M-Retrieve.conf and the response M-Acknowledge.ind PDUs.
@@ -1560,17 +1587,19 @@ MmsService.prototype = {
         }).bind(this));
       };
       // Update the delivery status to pending in DB.
       gMobileMessageDatabaseService
         .setMessageDelivery(aMessageId,
                             null,
                             null,
                             DELIVERY_STATUS_PENDING,
-                            this.retrieveMessage(url, responseNotify.bind(this)));
+                            this.retrieveMessage(url,
+                                                 responseNotify.bind(this),
+                                                 aDomMessage));
     }).bind(this));
   },
 
   // nsIWapPushApplication
 
   receiveWapPush: function receiveWapPush(array, length, offset, options) {
     let data = {array: array, offset: offset};
     let msg = MMS.PduHelper.parse(data, null);
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -1267,102 +1267,107 @@ MobileMessageDatabaseService.prototype =
         }
         messageStore.put(messageRecord);
       };
     });
   },
 
   getMessageRecordByTransactionId: function getMessageRecordByTransactionId(aTransactionId, aCallback) {
     if (DEBUG) debug("Retrieving message with transaction ID " + aTransactionId);
+    let self = this;
     this.newTxn(READ_ONLY, function (error, txn, messageStore) {
       if (error) {
         if (DEBUG) debug(error);
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
         return;
       }
       let request = messageStore.index("transactionId").get(aTransactionId);
 
       txn.oncomplete = function oncomplete(event) {
         if (DEBUG) debug("Transaction " + txn + " completed.");
         let messageRecord = request.result;
         if (!messageRecord) {
           if (DEBUG) debug("Transaction ID " + aTransactionId + " not found");
-          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null);
+          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null, null);
           return;
         }
-        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR, messageRecord);
+        // In this case, we don't need a dom message. Just pass null to the
+        // third argument.
+        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR,
+                         messageRecord, null);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) {
           if (event.target)
             debug("Caught error on transaction", event.target.errorCode);
         }
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
       };
     });
   },
 
   getMessageRecordById: function getMessageRecordById(aMessageId, aCallback) {
     if (DEBUG) debug("Retrieving message with ID " + aMessageId);
+    let self = this;
     this.newTxn(READ_ONLY, function (error, txn, messageStore) {
       if (error) {
         if (DEBUG) debug(error);
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
         return;
       }
       let request = messageStore.mozGetAll(aMessageId);
 
       txn.oncomplete = function oncomplete() {
         if (DEBUG) debug("Transaction " + txn + " completed.");
         if (request.result.length > 1) {
           if (DEBUG) debug("Got too many results for id " + aMessageId);
-          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null);
+          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null, null);
           return;
         }
         let messageRecord = request.result[0];
         if (!messageRecord) {
           if (DEBUG) debug("Message ID " + aMessageId + " not found");
-          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null);
+          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null, null);
           return;
         }
         if (messageRecord.id != aMessageId) {
           if (DEBUG) {
             debug("Requested message ID (" + aMessageId + ") is " +
                   "different from the one we got");
           }
-          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null);
+          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null, null);
           return;
         }
-        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR, messageRecord);
+        let domMessage = self.createDomMessageFromRecord(messageRecord);
+        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR,
+                         messageRecord, domMessage);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) {
           if (event.target) {
             debug("Caught error on transaction", event.target.errorCode);
           }
         }
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
       };
     });
   },
 
   /**
    * nsIMobileMessageDatabaseService API
    */
 
   getMessage: function getMessage(aMessageId, aRequest) {
     if (DEBUG) debug("Retrieving message with ID " + aMessageId);
-    let self = this;
     let notifyCallback = {
-      notify: function notify(aRv, aMessageRecord) {
+      notify: function notify(aRv, aMessageRecord, aDomMessage) {
         if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR == aRv) {
-          let domMessage = self.createDomMessageFromRecord(aMessageRecord);
-          aRequest.notifyMessageGot(domMessage);
+          aRequest.notifyMessageGot(aDomMessage);
           return;
         }
         aRequest.notifyGetMessageFailed(aRv, null);
       }
     };
     this.getMessageRecordById(aMessageId, notifyCallback);
   },
 
--- a/dom/permission/tests/file_framework.js
+++ b/dom/permission/tests/file_framework.js
@@ -6,21 +6,23 @@
  *
  * Fields in gData object
  * perms   (required) Array of Strings
  *         list of permissions that this test will need. See
  *         http://mxr.mozilla.org/mozilla-central/source/dom/apps/src/PermissionsTable.jsm
  *         These permissions are added after a sanity check and removed at
  *         test conclusion
  *
- * obj     (required) String
+ * obj     (required for default verifier) String
  *         The name of the window.navigator object used for accessing the
  *         WebAPI during the tests
  *
- * idl     (required) String
+ * webidl  (required for default verifier) String
+ * idl     (required for default verifier) String
+ *         Only one of webidl / idl is required
  *         The IDL describing the navigator object. The returned object
  *         during tests /must/ be an instanceof this
  *
  * skip    (optional) Array of Strings
  *         A list of navigator.userAgent's to skip the second part of tests
  *         on. The tests still verify that you can't get obj on those
  *         platforms without permissions, however it is expected that adding
  *         the permission still won't allow access to those objects
--- a/dom/permission/tests/file_shim.html
+++ b/dom/permission/tests/file_shim.html
@@ -6,22 +6,30 @@ function TestData(aOpts) {
     if (aOpts.hasOwnProperty(opt)) {
       this[opt] = aOpts[opt];
     }
   }
 }
 
 TestData.prototype = {
   getObj: function() {
-    if (!this.obj || !this.idl) {
+    if (!this.obj) {
+      return null;
+    }
+
+    // only one of the 2 should be set
+    if ((this.idl && this.webidl) ||
+        (!this.idl && !this.webidl)) {
       return null;
     }
 
     var obj = window.navigator[this.obj];
-    if (obj instanceof SpecialPowers.Ci[this.idl]) {
+
+    if ((this.webidl && obj instanceof window[this.webidl]) ||
+        (this.idl && obj instanceof SpecialPowers.Ci[this.idl])) {
       return obj;
     } else {
       return null;
     }
   },
 
   // default verifier
   verifier: function(success, failure) {
--- a/dom/phonenumberutils/PhoneNumber.jsm
+++ b/dom/phonenumberutils/PhoneNumber.jsm
@@ -234,29 +234,31 @@ this.PhoneNumber = (function (dataBase) 
     'm': 6, 'n': 6, 'o': 6,
     'p': 7, 'q': 7, 'r': 7, 's': 7,
     't': 8, 'u': 8, 'v': 8,
     'w': 9, 'x': 9, 'y': 9, 'z': 9
   };
 
   // Normalize a number by converting unicode numbers and symbols to their
   // ASCII equivalents and removing all non-dialable characters.
-  function NormalizeNumber(number) {
+  function NormalizeNumber(number, numbersOnly) {
     if (typeof number !== 'string') {
       return '';
     }
 
     number = number.replace(UNICODE_DIGITS,
                             function (ch) {
                               return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf));
                             });
-    number = number.replace(VALID_ALPHA_PATTERN,
-                            function (ch) {
-                              return String(E161[ch.toLowerCase()] || 0);
-                            });
+    if (!numbersOnly) {
+      number = number.replace(VALID_ALPHA_PATTERN,
+                              function (ch) {
+                                return String(E161[ch.toLowerCase()] || 0);
+                              });
+    }
     number = number.replace(LEADING_PLUS_CHARS_PATTERN, "+");
     number = number.replace(NON_DIALABLE_CHARS, "");
     return number;
   }
 
   // Check whether the number is valid for the given region.
   function IsValidNumber(number, md) {
     return md.possiblePattern.test(number);
--- a/dom/push/src/PushService.jsm
+++ b/dom/push/src/PushService.jsm
@@ -288,19 +288,17 @@ this.PushService = {
       case "final-ui-startup":
         Services.obs.removeObserver(this, "final-ui-startup");
         this.init();
         break;
       case "profile-change-teardown":
         Services.obs.removeObserver(this, "profile-change-teardown");
         this._shutdown();
         break;
-      case "network-interface-state-changed":
-        debug("network-interface-state-changed");
-
+      case "network-active-changed":
         if (this._udpServer) {
           this._udpServer.close();
         }
 
         this._shutdownWS();
 
         // Check to see if we need to do anything.
         this._db.getAllChannelIDs(function(channelIDs) {
@@ -404,19 +402,30 @@ this.PushService = {
   _willBeWokenUpByUDP: false,
 
   init: function() {
     debug("init()");
     if (!prefs.get("enabled"))
         return null;
 
     Services.obs.addObserver(this, "profile-change-teardown", false);
-    Services.obs.addObserver(this, "network-interface-state-changed",
-                             false);
     Services.obs.addObserver(this, "webapps-uninstall", false);
+
+    // This observer is notified only on B2G by
+    // dom/system/gonk/NetworkManager.js.
+    //
+    // The "active network" is based on priority - i.e. Wi-Fi has higher
+    // priority than data. The PushService should just use the preferred
+    // network, and not care about all interface changes.
+    // network-active-changed is not fired when the network goes offline, but
+    // socket connections time out. The check for Services.io.offline in
+    // _beginWSSetup() prevents unnecessary retries.  When the network comes
+    // back online, network-active-changed is fired.
+    Services.obs.addObserver(this, "network-active-changed", false);
+
     this._db = new PushDB(this);
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
 
     kCHILD_PROCESS_MESSAGES.forEach(function addMessage(msgName) {
         ppmm.addMessageListener(msgName, this);
     }.bind(this));
@@ -459,18 +468,17 @@ this.PushService = {
 
     this._waitingForPong = false;
     this._stopAlarm();
   },
 
   _shutdown: function() {
     debug("_shutdown()");
 
-    Services.obs.removeObserver(this, "network-interface-state-changed",
-                                false);
+    Services.obs.removeObserver(this, "network-active-changed");
     Services.obs.removeObserver(this, "webapps-uninstall", false);
 
     if (this._db) {
       this._db.close();
       this._db = null;
     }
 
     if (this._udpServer) {
@@ -530,16 +538,21 @@ this.PushService = {
       debug("_beginWSSetup: Not in shutdown state! Current state " +
             this._currentState);
       return;
     }
 
     // Stop any pending reconnects scheduled for the near future.
     this._stopAlarm();
 
+    if (Services.io.offline) {
+      debug("Network is offline.");
+      return;
+    }
+
     var serverURL = prefs.get("serverURL");
     if (!serverURL) {
       debug("No services.push.serverURL found!");
       return;
     }
 
     var uri;
     try {
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -325,20 +325,20 @@ SettingsManager.prototype = {
     let msg = aMessage.json;
 
     switch (aMessage.name) {
       case "Settings:Change:Return:OK":
         if (this._onsettingchange || this._callbacks) {
           if (DEBUG) debug('data:' + msg.key + ':' + msg.value + '\n');
 
           if (this._onsettingchange) {
-            let event = new this._window.MozSettingsEvent("settingchanged", {
+            let event = new this._window.MozSettingsEvent("settingchanged", this._wrap({
               settingName: msg.key,
               settingValue: msg.value
-            });
+            }));
             this._onsettingchange.handleEvent(event);
           }
           if (this._callbacks && this._callbacks[msg.key]) {
             if (DEBUG) debug("observe callback called! " + msg.key + " " + this._callbacks[msg.key].length);
             this._callbacks[msg.key].forEach(function(cb) {
               cb(this._wrap({settingName: msg.key, settingValue: msg.value}));
             }.bind(this));
           }
--- a/dom/settings/tests/test_settings_onsettingchange.html
+++ b/dom/settings/tests/test_settings_onsettingchange.html
@@ -61,16 +61,27 @@ function observerWithNext(setting) {
 
 function onsettingschangeWithNext(event) {
   dump("onsettingschangewithnext called!\n");
   is(event.settingName, "screen.brightness", "Same settingName");
   is(event.settingValue, "0.7", "Same settingvalue");
   next();
 };
 
+var cset = {'a':'b','c':[{'d':'e'}]};
+
+function onComplexSettingschangeWithNext(event) {
+  is(event.settingName, "test.key", "Same settingName");
+  is(event.settingValue['a'], "b", "Same settingvalue");
+  var c = event.settingValue['c'];
+  ok(Array.isArray(c), "c is array!");
+  is(c[0]['d'], 'e', "Right settingValue!");
+  next();
+};
+
 var req, req2;
 var index = 0;
 
 var mozSettings = window.navigator.mozSettings;
 
 var steps = [
   function () {
     ok(true, "Deleting database");
@@ -246,16 +257,32 @@ var steps = [
     req = lock.clear();
     req.onsuccess = function () {
       ok(true, "Deleted the database");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
+    ok(true, "Deleting database");
+    var lock = mozSettings.createLock();
+    req = lock.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      navigator.mozSettings.onsettingchange = onComplexSettingschangeWithNext;
+    };
+    req.onerror = onFailure;
+    req2 = navigator.mozSettings.createLock().set({'test.key': cset});
+    req2.onsuccess = function () {
+      ok(true, "set done");
+    }
+    req2.onerror = onFailure;
+  },
+  
+  function () {
     ok(true, "all done!\n");
     SimpleTest.finish();
   }
 ];
 
 function next() {
   ok(true, "Begin!");
   if (index >= steps.length) {
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -188,21 +188,21 @@ AudioManager::Observe(nsISupports* aSubj
                       const char* aTopic,
                       const PRUnichar* aData)
 {
   if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED)) {
     if (NS_strlen(aData) > 0) {
       String8 cmd;
       cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
       AudioSystem::setParameters(0, cmd);
-      const char* address = NS_ConvertUTF16toUTF8(nsDependentString(aData)).get();
+      NS_ConvertUTF16toUTF8 address(aData);
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
-                                            AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address);
+                                            AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.get());
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
-                                            AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address);
+                                            AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.get());
       SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
     } else {
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
                                             AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
                                             AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
       // only force to none if the current force setting is bt_sco
       int32_t force;
--- a/dom/system/gonk/Makefile.in
+++ b/dom/system/gonk/Makefile.in
@@ -38,16 +38,18 @@ LOCAL_INCLUDES = \
   $(NULL)
 
 EXTRA_COMPONENTS = \
   NetworkManager.manifest \
   NetworkManager.js \
   RadioInterfaceLayer.manifest \
   RadioInterfaceLayer.js \
   RILContentHelper.js \
+  NetworkInterfaceListService.manifest \
+  NetworkInterfaceListService.js \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   net_worker.js \
   ril_consts.js \
   ril_worker.js \
   systemlibs.js \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/NetworkInterfaceListService.js
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const NETWORKLISTSERVICE_CONTRACTID =
+        "@mozilla.org/network/interface-list-service;1";
+const NETWORKLISTSERVICE_CID =
+        Components.ID("{3780be6e-7012-4e53-ade6-15212fb88a0d}");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsISyncMessageSender");
+
+function NetworkInterfaceListService () {
+}
+
+NetworkInterfaceListService.prototype = {
+  classID: NETWORKLISTSERVICE_CID,
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceListService]),
+
+  getDataInterfaceList: function(aConditions) {
+    return new NetworkInterfaceList(
+      cpmm.sendSyncMessage(
+        'NetworkInterfaceList:ListInterface',
+        {
+          excludeSupl: (aConditions &
+                        Ci.nsINetworkInterfaceListService.
+                        LIST_NOT_INCLUDE_SUPL_INTERFACES) != 0,
+          excludeMms: (aConditions &
+                       Ci.nsINetworkInterfaceListService.
+                       LIST_NOT_INCLUDE_MMS_INTERFACES) != 0
+        }
+      )[0]);
+  }
+};
+
+function NetworkInterfaceList (aInterfaces) {
+  this._interfaces = aInterfaces;
+}
+
+NetworkInterfaceList.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceList]),
+  getNumberOfInterface: function() {
+    return this._interfaces.length;
+  },
+
+  getInterface: function(index) {
+    if (!this._interfaces) {
+      return null;
+    }
+    return this._interfaces[index];
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkInterfaceListService]);
+
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/NetworkInterfaceListService.manifest
@@ -0,0 +1,17 @@
+# Copyright 2012 Mozilla Foundation and Mozilla contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# NetworkInterfaceListService.js
+component {3780be6e-7012-4e53-ade6-15212fb88a0d} NetworkInterfaceListService.js
+contract @mozilla.org/network/interface-list-service;1 {3780be6e-7012-4e53-ade6-15212fb88a0d}
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -17,16 +17,24 @@ const NETWORKINTERFACE_CONTRACTID = "@mo
 const NETWORKINTERFACE_CID =
   Components.ID("{266c3edd-78f0-4512-8178-2d6fee2d35ee}");
 
 const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"]
+         .getService(Ci.nsIMessageBroadcaster);
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
+                                   "@mozilla.org/network/dns-service;1",
+                                   "nsIDNSService");
 
 const TOPIC_INTERFACE_STATE_CHANGED  = "network-interface-state-changed";
 const TOPIC_INTERFACE_REGISTERED     = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED   = "network-interface-unregistered";
 const TOPIC_ACTIVE_CHANGED           = "network-active-changed";
 const TOPIC_MOZSETTINGS_CHANGED      = "mozsettings-changed";
 const TOPIC_PREF_CHANGED             = "nsPref:changed";
 const TOPIC_XPCOM_SHUTDOWN           = "xpcom-shutdown";
@@ -124,16 +132,18 @@ function isComplete(code) {
 
 /**
  * This component watches for network interfaces changing state and then
  * adjusts routes etc. accordingly.
  */
 function NetworkManager() {
   this.networkInterfaces = {};
   Services.obs.addObserver(this, TOPIC_INTERFACE_STATE_CHANGED, true);
+  Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, true);
+  Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, true);
   Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
   Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
 
   debug("Starting worker.");
   this.worker = new ChromeWorker("resource://gre/modules/net_worker.js");
   this.worker.onmessage = this.handleWorkerMessage.bind(this);
   this.worker.onerror = function onerror(event) {
     debug("Received error from worker: " + event.filename +
@@ -199,16 +209,18 @@ function NetworkManager() {
         settingsLock.set(SETTINGS_WIFI_ENABLED, aResult, null);
       });
     },
 
     handleError: function (aErrorMessage) {
       debug("Error reading the 'tethering.wifi.enabled' setting: " + aErrorMessage);
     }
   });
+
+  ppmm.addMessageListener('NetworkInterfaceList:ListInterface', this);
 }
 NetworkManager.prototype = {
   classID:   NETWORKMANAGER_CID,
   classInfo: XPCOMUtils.generateCI({classID: NETWORKMANAGER_CID,
                                     contractID: NETWORKMANAGER_CONTRACTID,
                                     classDescription: "Network Manager",
                                     interfaces: [Ci.nsINetworkManager]}),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager,
@@ -268,33 +280,89 @@ NetworkManager.prototype = {
             this.setAndConfigureActive();
             // Update data connection when Wifi connected/disconnected
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
               this.mRIL.updateRILNetworkInterface();
             }
             break;
         }
         break;
+      case TOPIC_INTERFACE_REGISTERED:
+        let regNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
+        debug("Network '" + regNetwork.name + "' registered, adding mmsproxy and/or mmsc route");
+        if (regNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
+	  let mmsHosts = this.resolveHostname(
+	      [ Services.prefs.getCharPref("ril.mms.mmsproxy"),
+                Services.prefs.getCharPref("ril.mms.mmsc") ]
+	    );
+          this.addHostRouteWithResolve(regNetwork, mmsHosts);
+        }
+        break;
+      case TOPIC_INTERFACE_UNREGISTERED:
+        let unregNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
+        debug("Network '" + regNetwork.name + "' unregistered, removing mmsproxy and/or mmsc route");
+        if (unregNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
+	  let mmsHosts = this.resolveHostname(
+	      [ Services.prefs.getCharPref("ril.mms.mmsproxy"),
+                Services.prefs.getCharPref("ril.mms.mmsc") ]
+	    );
+          this.removeHostRouteWithResolve(unregNetwork, mmsHosts);
+        }
+        break;
       case TOPIC_MOZSETTINGS_CHANGED:
         let setting = JSON.parse(data);
         this.handle(setting.key, setting.value);
         break;
       case TOPIC_PREF_CHANGED:
         this._manageOfflineStatus =
           Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
         debug(PREF_MANAGE_OFFLINE_STATUS + " has changed to " + this._manageOfflineStatus);
         break;
       case TOPIC_XPCOM_SHUTDOWN:
         Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
         Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
+        Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
+        Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
         Services.obs.removeObserver(this, TOPIC_INTERFACE_STATE_CHANGED);
         break;
     }
   },
 
+  receiveMessage: function receiveMessage(aMsg) {
+    switch (aMsg.name) {
+      case "NetworkInterfaceList:ListInterface": {
+        let excludeMms = aMsg.json.exculdeMms;
+        let excludeSupl = aMsg.json.exculdeSupl;
+        let interfaces = [];
+
+        for each (let i in this.networkInterfaces) {
+          if ((i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS && excludeMms) ||
+              (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL && excludeSupl)) {
+            continue;
+          }
+          interfaces.push({
+            state: i.state,
+            type: i.type,
+            name: i.name,
+            dhcp: i.dhcp,
+            ip: i.ip,
+            netmask: i.netmask,
+            broadcast: i.broadcast,
+            gateway: i.gateway,
+            dns1: i.dns1,
+            dns2: i.dns2,
+            httpProxyHost: i.httpProxyHost,
+            httpProxyPort: i.httpProxyPort
+          });
+        }
+        return interfaces;
+      }
+    }
+  },
+
   // nsINetworkManager
 
   registerNetworkInterface: function registerNetworkInterface(network) {
     if (!(network instanceof Ci.nsINetworkInterface)) {
       throw Components.Exception("Argument must be nsINetworkInterface.",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     if (network.name in this.networkInterfaces) {
@@ -518,35 +586,70 @@ NetworkManager.prototype = {
     this.worker.postMessage(options);
   },
 
   addHostRoute: function addHostRoute(network) {
     debug("Going to add host route on " + network.name);
     let options = {
       cmd: "addHostRoute",
       ifname: network.name,
-      dns1: network.dns1,
-      dns2: network.dns2,
       gateway: network.gateway,
-      httpproxy: network.httpProxyHost,
-      mmsproxy: Services.prefs.getCharPref("ril.mms.mmsproxy")
+      hostnames: [network.dns1, network.dns2, network.httpProxyHost]
     };
     this.worker.postMessage(options);
   },
 
   removeHostRoute: function removeHostRoute(network) {
     debug("Going to remove host route on " + network.name);
     let options = {
       cmd: "removeHostRoute",
       ifname: network.name,
-      dns1: network.dns1,
-      dns2: network.dns2,
       gateway: network.gateway,
-      httpproxy: network.httpProxyHost,
-      mmsproxy: Services.prefs.getCharPref("ril.mms.mmsproxy")
+      hostnames: [network.dns1, network.dns2, network.httpProxyHost]
+    };
+    this.worker.postMessage(options);
+  },
+
+  resolveHostname: function resolveHostname(hosts) {
+    let retval = [];
+
+    for(var i = 0; i < hosts.length; i++) {
+      let hostname = hosts[i].split('/')[2];
+      if (!hostname) {
+        continue;
+      }
+
+      let hostnameIps = gDNSService.resolve(hostname, 0);
+      while (hostnameIps.hasMore()) {
+        retval.push(hostnameIps.getNextAddrAsString());
+        debug("Found IP at: " + JSON.stringify(retval));
+      }
+    }
+
+    return retval;
+  },
+
+  addHostRouteWithResolve: function addHostRouteWithResolve(network, hosts) {
+    debug("Going to add host route after dns resolution on " + network.name);
+    let options = {
+      cmd: "addHostRoute",
+      ifname: network.name,
+      gateway: network.gateway,
+      hostnames: hosts
+    };
+    this.worker.postMessage(options);
+  },
+
+  removeHostRouteWithResolve: function removeHostRouteWithResolve(network, hosts) {
+    debug("Going to remove host route after dns resolution on " + network.name);
+    let options = {
+      cmd: "removeHostRoute",
+      ifname: network.name,
+      gateway: network.gateway,
+      hostnames: hosts
     };
     this.worker.postMessage(options);
   },
 
   setNetworkProxy: function setNetworkProxy() {
     try {
       if (!this.active.httpProxyHost || this.active.httpProxyHost == "") {
         // Sets direct connection to internet.
--- a/dom/system/gonk/TimeZoneSettingObserver.cpp
+++ b/dom/system/gonk/TimeZoneSettingObserver.cpp
@@ -127,17 +127,17 @@ TimeZoneSettingObserver::TimeZoneSetting
 nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
 {
   // Convert the JS value to a nsCString type.
   nsDependentJSString valueStr;
   if (!valueStr.init(aContext, aValue.toString())) {
     ERR("Failed to convert JS value to nsCString");
     return NS_ERROR_FAILURE;
   }
-  nsCString newTimezone = NS_ConvertUTF16toUTF8(valueStr);
+  NS_ConvertUTF16toUTF8 newTimezone(valueStr);
 
   // Set the timezone only when the system timezone is not identical.
   nsCString curTimezone = hal::GetTimezone();
   if (!curTimezone.Equals(newTimezone)) {
     hal::SetTimezone(newTimezone);
   }
 
   return NS_OK;
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -12,16 +12,17 @@
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 XPIDL_SOURCES += [
     'nsIAudioManager.idl',
     'nsINavigatorAudioChannelManager.idl',
+    'nsINetworkInterfaceListService.idl',
     'nsINetworkManager.idl',
     'nsIRadioInterfaceLayer.idl',
     'nsISystemWorkerManager.idl',
     'nsIVolume.idl',
     'nsIVolumeMountLock.idl',
     'nsIVolumeService.idl',
     'nsIVolumeStat.idl',
     'nsIWorkerHolder.idl',
--- a/dom/system/gonk/net_worker.js
+++ b/dom/system/gonk/net_worker.js
@@ -224,30 +224,28 @@ function runDHCPAndSetDefaultRouteAndDNS
 function removeDefaultRoute(options) {
   libnetutils.ifc_remove_default_route(options.ifname);
 }
 
 /**
  * Add host route for given network interface.
  */
 function addHostRoute(options) {
-  libnetutils.ifc_add_route(options.ifname, options.dns1, 32, options.gateway);
-  libnetutils.ifc_add_route(options.ifname, options.dns2, 32, options.gateway);
-  libnetutils.ifc_add_route(options.ifname, options.httpproxy, 32, options.gateway);
-  libnetutils.ifc_add_route(options.ifname, options.mmsproxy, 32, options.gateway);
+  for (let i = 0; i < options.hostnames.length; i++) {
+    libnetutils.ifc_add_route(options.ifname, options.hostnames[i], 32, options.gateway);
+  }
 }
 
 /**
  * Remove host route for given network interface.
  */
 function removeHostRoute(options) {
-  libnetutils.ifc_remove_route(options.ifname, options.dns1, 32, options.gateway);
-  libnetutils.ifc_remove_route(options.ifname, options.dns2, 32, options.gateway);
-  libnetutils.ifc_remove_route(options.ifname, options.httpproxy, 32, options.gateway);
-  libnetutils.ifc_remove_route(options.ifname, options.mmsproxy, 32, options.gateway);
+  for (let i = 0; i < options.hostnames.length; i++) {
+    libnetutils.ifc_remove_route(options.ifname, options.hostnames[i], 32, options.gateway);
+  }
 }
 
 function removeNetworkRoute(options) {
   let ipvalue = netHelpers.stringToIP(options.ip);
   let netmaskvalue = netHelpers.stringToIP(options.netmask);
   let subnet = netmaskvalue & ipvalue;
   let dst = netHelpers.ipToString(subnet);
   let prefixLength = netHelpers.getMaskLength(netmaskvalue);
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsINetworkInterfaceListService.idl
@@ -0,0 +1,36 @@
+/* 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 "nsINetworkManager.idl"
+#include "nsISupports.idl"
+
+[scriptable, uuid(b44d74db-c9d6-41dd-98ae-a56918d6e6ad)]
+interface nsINetworkInterfaceList : nsISupports
+{
+  /**
+   * Number of the network interfaces that is available.
+   */
+  long getNumberOfInterface();
+
+  /**
+   * Get the i-th interface from the list.
+   * @param interfaceIndex index of interface, from 0 to number of interface - 1.
+   */
+  nsINetworkInterface getInterface(in long interfaceIndex);
+};
+
+[scriptable, uuid(5be50bcb-bfe9-4742-b7e6-3e9bb4835369)]
+interface nsINetworkInterfaceListService : nsISupports
+{
+  const long LIST_NOT_INCLUDE_MMS_INTERFACES = 1;
+  const long LIST_NOT_INCLUDE_SUPL_INTERFACES = 2;
+
+  /**
+   * Obtain a list of network interfaces that satisfy the specified condition.
+   * @param condition flags that specify the interfaces to be returned. This
+   *        can be OR combination of LIST_* flags, or zero to make all available
+   *        interfaces returned.
+   */
+  nsINetworkInterfaceList getDataInterfaceList(in long condition);
+};
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -182,34 +182,34 @@ NS_IMETHODIMP nsVolumeService::GetVolume
 
   vol.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
 {
-  nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
+  NS_ConvertUTF16toUTF8 utf8Path(aPath);
   char realPathBuf[PATH_MAX];
 
   while (realpath(utf8Path.get(), realPathBuf) < 0) {
     if (errno != ENOENT) {
       ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
       return NSRESULT_FOR_ERRNO();
     }
     // The pathname we were passed doesn't exist, so we try stripping off trailing
     // components until we get a successful call to realpath, or until we run out
     // of components (if we finally get to /something then we also stop).
     int32_t slashIndex = utf8Path.RFindChar('/');
     if ((slashIndex == kNotFound) || (slashIndex == 0)) {
       errno = ENOENT;
       ERR("GetVolumeByPath: realpath on '%s' failed.", utf8Path.get());
       return NSRESULT_FOR_ERRNO();
     }
-    utf8Path = Substring(utf8Path, 0, slashIndex);
+    utf8Path.Assign(Substring(utf8Path, 0, slashIndex));
   }
 
   // The volume mount point is always a directory. Something like /mnt/sdcard
   // Once we have a full qualified pathname with symlinks removed (which is
   // what realpath does), we basically check if aPath starts with the mount
   // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
   // do want it to match /mnt/sdcard/foo
   // So we add a trailing slash to the mount point and the pathname passed in
@@ -218,17 +218,17 @@ nsVolumeService::GetVolumeByPath(const n
   strlcat(realPathBuf, "/", sizeof(realPathBuf));
 
   MonitorAutoLock autoLock(mArrayMonitor);
 
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
-    nsAutoCString volMountPointSlash = NS_ConvertUTF16toUTF8(vol->MountPoint());
+    NS_ConvertUTF16toUTF8 volMountPointSlash(vol->MountPoint());
     volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
     nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
     if (volMountPointSlash.Equals(testStr)) {
       vol.forget(aResult);
       return NS_OK;
     }
   }
   return NS_ERROR_FILE_NOT_FOUND;
--- a/dom/system/gonk/nsVolumeStat.cpp
+++ b/dom/system/gonk/nsVolumeStat.cpp
@@ -7,19 +7,17 @@
 
 namespace mozilla {
 namespace system {
 
 NS_IMPL_ISUPPORTS1(nsVolumeStat, nsIVolumeStat)
 
 nsVolumeStat::nsVolumeStat(const nsAString& aPath)
 {
-  nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
-
-  if (statfs(utf8Path.get(), &mStat) != 0) {
+  if (statfs(NS_ConvertUTF16toUTF8(aPath).get(), &mStat) != 0) {
     memset(&mStat, 0, sizeof(mStat));
   }
 }
 
 nsVolumeStat::~nsVolumeStat()
 {
 }
 
--- a/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
@@ -64,16 +64,21 @@ function verifyInitialState(confirmNoCal
 }
 
 function simulateIncoming() {
   log("Simulating an incoming call.");
 
   telephony.oncallschanged = function oncallschanged(event) {
     log("Received 'callschanged' event.");
 
+    if (!event.call) {
+      log("Notifying calls array is loaded. No call information accompanies.");
+      return;
+    }
+
     let expected_states = ["incoming", "disconnected"];
     ok(expected_states.indexOf(event.call.state) != -1,
       "Unexpected call state: " + event.call.state);
 
     if (event.call.state == "incoming") {
       log("Received 'callschanged' event for an incoming call.");
       incoming = event.call;
       ok(incoming);
--- a/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
@@ -64,16 +64,21 @@ function verifyInitialState(confirmNoCal
 }
 
 function dial() {
   log("Make an outgoing call.");
 
   telephony.oncallschanged = function oncallschanged(event) {
     log("Received 'callschanged' call event.");
 
+    if (!event.call) {
+      log("Notifying calls array is loaded. No call information accompanies.");
+      return;
+    }
+
     let expected_states = ["dialing", "disconnected"];
     ok(expected_states.indexOf(event.call.state) != -1,
       "Unexpected call state: " + event.call.state);
 
     if (event.call.state == "dialing") {
       outgoing = event.call;
       ok(outgoing);
       is(outgoing.number, number);
--- a/dom/webidl/BiquadFilterNode.webidl
+++ b/dom/webidl/BiquadFilterNode.webidl
@@ -6,16 +6,19 @@
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 enum BiquadFilterType {
+  // Hack: Use numbers to support alternate enum values
+  "0", "1", "2", "3", "4", "5", "6", "7",
+
   "lowpass",
   "highpass",
   "bandpass",
   "lowshelf",
   "highshelf",
   "peaking",
   "notch",
   "allpass"
@@ -31,8 +34,24 @@ interface BiquadFilterNode : AudioNode {
     readonly attribute AudioParam gain; // in Decibels
 
     void getFrequencyResponse(Float32Array frequencyHz,
                               Float32Array magResponse,
                               Float32Array phaseResponse);
 
 };
 
+/*
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
+ */
+[PrefControlled]
+partial interface BiquadFilterNode {
+    const unsigned short LOWPASS = 0;
+    const unsigned short HIGHPASS = 1;
+    const unsigned short BANDPASS = 2;
+    const unsigned short LOWSHELF = 3;
+    const unsigned short HIGHSHELF = 4;
+    const unsigned short PEAKING = 5;
+    const unsigned short NOTCH = 6;
+    const unsigned short ALLPASS = 7;
+};
+
--- a/dom/webidl/DataChannel.webidl
+++ b/dom/webidl/DataChannel.webidl
@@ -41,10 +41,12 @@ interface DataChannel : EventTarget
   void send(ArrayBufferView data);
 };
 
 // Mozilla extensions.
 partial interface DataChannel
 {
   readonly attribute DOMString protocol;
   readonly attribute boolean ordered;
-  readonly attribute unsigned short stream;
+  readonly attribute unsigned short id;
+  // this is deprecated due to renaming in the spec, but still supported for Fx22
+  readonly attribute unsigned short stream; // now id
 };
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -122,25 +122,16 @@ partial interface HTMLMediaElement {
            attribute unsigned long mozFrameBufferLength;
 
   // Mozilla extension: return embedded metadata from the stream as a
   // JSObject with key:value pairs for each tag. This can be used by
   // player interfaces to display the song title, artist, etc.
   [Throws]
   object? mozGetMetadata();
 
-  // Mozilla extension: load data from another media element. This is like
-  // load() but we don't run the resource selection algorithm; instead
-  // we just set our source to other's currentSrc. This is optimized
-  // so that this element will get access to all of other's cached/
-  // buffered data. In fact any future data downloaded by this element or
-  // other will be sharable by both elements.
-  [Throws]
-  void mozLoadFrom(HTMLMediaElement other);
-
   // Mozilla extension: provides access to the fragment end time if
   // the media element has a fragment URI for the currentSrc, otherwise
   // it is equal to the media duration.
   readonly attribute double mozFragmentEnd;
 
   // Mozilla extension: an audio channel type for media elements.
   // An exception is thrown if the app tries to change the audio channel type
   // without the permission (manifest file for B2G apps).
--- a/dom/webidl/PannerNode.webidl
+++ b/dom/webidl/PannerNode.webidl
@@ -6,21 +6,27 @@
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 enum PanningModelType {
+  // Hack: Use numbers to support alternate enum values
+  "0", "1",
+
   "equalpower",
   "HRTF"
 };
 
 enum DistanceModelType {
+  // Hack: Use numbers to support alternate enum values
+  "0", "1", "2",
+
   "linear",
   "inverse",
   "exponential"
 };
 
 [PrefControlled]
 interface PannerNode : AudioNode {
 
@@ -40,8 +46,22 @@ interface PannerNode : AudioNode {
 
     // Directional sound cone 
     attribute double coneInnerAngle;
     attribute double coneOuterAngle;
     attribute double coneOuterGain;
 
 };
 
+/*
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
+ */
+[PrefControlled]
+partial interface PannerNode {
+    const unsigned short EQUALPOWER = 0;
+    const unsigned short HRTF = 1;
+
+    const unsigned short LINEAR_DISTANCE = 0;
+    const unsigned short INVERSE_DISTANCE = 1;
+    const unsigned short EXPONENTIAL_DISTANCE = 2;
+};
+
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -32,22 +32,28 @@ enum RTCIceConnectionState {
     "connected",
     "completed",
     "failed",
     "disconnected",
     "closed"
 };
 
 dictionary RTCDataChannelInit {
-  boolean outOfOrderAllowed;
-  unsigned short maxRetransmitTime;
-  unsigned short maxRetransmitNum;
-  DOMString protocol;
-  boolean preset;
-  unsigned short stream;
+  boolean         ordered = true;
+  unsigned short? maxRetransmitTime = null;
+  unsigned short? maxRetransmits = null;
+  DOMString       protocol = "";
+  boolean         negotiated = false; // spec currently says 'true'; we disagree
+  unsigned short? id = null;
+
+  // these are deprecated due to renaming in the spec, but still supported for Fx22
+  boolean outOfOrderAllowed; // now ordered, and the default changes to keep behavior the same
+  unsigned short maxRetransmitNum; // now maxRetransmits
+  boolean preset; // now negotiated
+  unsigned short stream; // now id
 };
 
 interface RTCDataChannel;
 
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/peerconnection;1",
  Constructor (optional RTCConfiguration configuration,
               optional object? constraints)]
--- a/gfx/2d/Blur.cpp
+++ b/gfx/2d/Blur.cpp
@@ -461,17 +461,21 @@ AlphaBoxBlur::Blur(uint8_t* aData)
   if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
     int32_t stride = GetStride();
 
     IntSize size = GetSize();
 
     if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
       // No need to use CheckedInt here - we have validated it in the constructor.
       size_t szB = stride * size.height;
-      unsigned char* tmpData = new uint8_t[szB];
+      unsigned char* tmpData = new (std::nothrow) uint8_t[szB];
+
+      if (!tmpData) {
+        return;
+      }
 
       memset(tmpData, 0, szB);
 
       SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
       SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
 
       delete [] tmpData;
     }
@@ -521,16 +525,19 @@ AlphaBoxBlur::Blur(uint8_t* aData)
       delete [] tmpData;
     } else {
       size_t integralImageStride = GetAlignedStride<16>(integralImageSize.width * 4);
 
       // We need to leave room for an additional 12 bytes for a maximum overrun
       // of 3 pixels in the blurring code.
       AlignedArray<uint32_t> integralImage((integralImageStride / 4) * integralImageSize.height + 12);
 
+      if (!integralImage) {
+        return;
+      }
 #ifdef USE_SSE2
       if (Factory::HasSSE2()) {
         BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
                      verticalLobes[0][1], integralImage, integralImageStride);
         BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
                      verticalLobes[1][1], integralImage, integralImageStride);
         BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
                      verticalLobes[2][1], integralImage, integralImageStride);
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -36,16 +36,27 @@ struct IntRectTyped :
         Super(aPos, aSize) {}
     IntRectTyped(int32_t _x, int32_t _y, int32_t _width, int32_t _height) :
         Super(_x, _y, _width, _height) {}
 
     // Rounding isn't meaningful on an integer rectangle.
     void Round() {}
     void RoundIn() {}
     void RoundOut() {}
+
+    // XXX When all of the code is ported, the following functions to convert to and from
+    // unknown types should be removed.
+
+    static IntRectTyped<units> FromUnknownRect(const IntRectTyped<UnknownUnits>& rect) {
+        return IntRectTyped<units>(rect.x, rect.y, rect.width, rect.height);
+    }
+
+    IntRectTyped<UnknownUnits> ToUnknownRect() const {
+        return IntRectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
+    }
 };
 typedef IntRectTyped<UnknownUnits> IntRect;
 
 template<class units>
 struct RectTyped :
     public BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin>,
     public units {
     typedef BaseRect<Float, RectTyped<units>, PointTyped<units>, SizeTyped<units>, Margin> Super;
@@ -70,15 +81,26 @@ struct RectTyped :
     bool ToIntRect(IntRectTyped<units> *aOut) const
     {
       *aOut = IntRectTyped<units>(int32_t(this->X()), int32_t(this->Y()),
                                   int32_t(this->Width()), int32_t(this->Height()));
       return RectTyped<units>(Float(aOut->x), Float(aOut->y), 
                               Float(aOut->width), Float(aOut->height))
              .IsEqualEdges(*this);
     }
+
+    // XXX When all of the code is ported, the following functions to convert to and from
+    // unknown types should be removed.
+
+    static RectTyped<units> FromUnknownRect(const RectTyped<UnknownUnits>& rect) {
+        return RectTyped<units>(rect.x, rect.y, rect.width, rect.height);
+    }
+
+    RectTyped<UnknownUnits> ToUnknownRect() const {
+        return RectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
+    }
 };
 typedef RectTyped<UnknownUnits> Rect;
 
 }
 }
 
 #endif /* MOZILLA_GFX_RECT_H_ */
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -105,17 +105,17 @@ struct AlignedArray
   {
     delete [] mStorage;
     mStorage = mPtr = nullptr;
   }
 
   MOZ_ALWAYS_INLINE void Realloc(size_t aSize)
   {
     delete [] mStorage;
-    mStorage = new T[aSize + (alignment - 1)];
+    mStorage = new (std::nothrow) T[aSize + (alignment - 1)];
     if (uintptr_t(mStorage) % alignment) {
       // Our storage does not start at a <alignment>-byte boundary. Make sure mData does!
       mPtr = (uint32_t*)(uintptr_t(mStorage) +
         (alignment - (uintptr_t(mStorage) % alignment)));
     } else {
       mPtr = mStorage;
     }
   }
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -252,17 +252,17 @@ public:
    * be ignored, but compositor implementations are free to use it if they like.
    */
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) = 0;
 
   /**
    * Declare an offset to use when rendering layers. This will be ignored when
    * rendering to a target instead of the screen.
    */
-  virtual void SetScreenRenderOffset(const gfx::Point& aOffset) = 0;
+  virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
 
   /**
    * Tell the compositor to actually draw a quad. What to do draw and how it is
    * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
    * aTransform transforms from user space to screen space. aOffset is the
    * offset of the render target from 0,0 of the screen. If texture coords are
    * required, these will be in the primary effect in the effect chain.
    */
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -115,28 +115,28 @@ public:
   // viewport dimensions to calculate the displayport, we'd run into situations
   // where we're prerendering the wrong regions and the content may be clipped,
   // or too much of it prerendered. If the displayport is the same as the
   // viewport, there is no need for this and we can just use the viewport
   // instead.
   //
   // This is only valid on the root layer. Nested iframes do not need this
   // metric as they do not have a displayport set. See bug 775452.
-  nsIntRect mCompositionBounds;
+  LayerIntRect mCompositionBounds;
 
-  // |mScrollableRect|, stored in device pixels. DECPRECATED, DO NOT USE.
+  // |mScrollableRect|, stored in layer pixels. DECPRECATED, DO NOT USE.
   //
   // This is valid on any layer where |mScrollableRect| is, though it may be
   // more lazily maintained than |mScrollableRect|. That is, when
   // |mScrollableRect| is updated, this may lag. For this reason, it's better to
   // use |mScrollableRect| for any control logic.
   //
   // FIXME/bug 785929: Is this really necessary? Can it not be calculated from
   // |mScrollableRect| whenever it's needed?
-  nsIntRect mContentRect;
+  LayerIntRect mContentRect;
 
   // ---------------------------------------------------------------------------
   // The following metrics are all in CSS pixels. They are not in any uniform
   // space, so each is explained separately.
   //
 
   // The area of a frame's contents that has been painted, relative to the
   // viewport. It is in the same coordinate space as |mViewport|. For example,
@@ -203,17 +203,17 @@ public:
   //   width = window.innerWidth, height = window.innerHeight }
   //
   // This is relative to the document. It is in the same coordinate space as
   // |mScrollOffset|, but a different coordinate space than |mViewport| and
   // |mDisplayPort|. Note also that this coordinate system is understood by
   // window.scrollTo().
   //
   // This is valid on any layer unless it has no content.
-  gfx::Rect mScrollableRect;
+  mozilla::CSSRect mScrollableRect;
 
   // ---------------------------------------------------------------------------
   // The following metrics are dimensionless.
   //
 
   // The resolution, along both axes, that the current frame has been painted
   // at.
   //
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -280,24 +280,31 @@ CreateCSSValueList(const InfallibleTArra
         arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
         arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
         break;
       }
       case TransformFunction::TSkewX:
       {
         float x = aFunctions[i].get_SkewX().x();
         arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewx, resultTail);
-        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+        arr->Item(1).SetFloatValue(x, eCSSUnit_Radian);
         break;
       }
       case TransformFunction::TSkewY:
       {
         float y = aFunctions[i].get_SkewY().y();
         arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewy, resultTail);
-        arr->Item(1).SetFloatValue(y, eCSSUnit_Number);
+        arr->Item(1).SetFloatValue(y, eCSSUnit_Radian);
+        break;
+      }
+      case TransformFunction::TSkew:
+      {
+        arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skew, resultTail);
+        arr->Item(1).SetFloatValue(aFunctions[i].get_Skew().x(), eCSSUnit_Radian);
+        arr->Item(2).SetFloatValue(aFunctions[i].get_Skew().y(), eCSSUnit_Radian);
         break;
       }
       case TransformFunction::TTransformMatrix:
       {
         arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_matrix3d, resultTail);
         const gfx3DMatrix& matrix = aFunctions[i].get_TransformMatrix().value();
         arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
         arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -78,17 +78,17 @@ public:
   virtual bool CanUseCanvasLayerForSize(const gfxIntSize &aSize) MOZ_OVERRIDE { return true; }
   virtual int32_t GetMaxTextureSize() const MOZ_OVERRIDE { return INT32_MAX; }
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) MOZ_OVERRIDE { }
   virtual void SetTargetContext(gfxContext* aTarget) MOZ_OVERRIDE
   {
     mCopyTarget = aTarget;
   }
   
-  virtual void SetScreenRenderOffset(const gfx::Point& aOffset) MOZ_OVERRIDE {
+  virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) MOZ_OVERRIDE {
   }
 
   virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) { }
 
   virtual void PrepareViewport(const gfx::IntSize& aSize,
                                const gfxMatrix& aWorldTransform) MOZ_OVERRIDE { }
 
   virtual void NotifyLayersTransaction() MOZ_OVERRIDE { }
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -21,25 +21,16 @@
 #include "nsAnimationManager.h"
 #include "mozilla/layers/AsyncPanZoomController.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace layers {
 
-void
-AsyncCompositionManager::SetTransformation(float aScale,
-                                           const nsIntPoint& aScrollOffset)
-{
-  mXScale = aScale;
-  mYScale = aScale;
-  mScrollOffset = aScrollOffset;
-}
-
 enum Op { Resolve, Detach };
 
 static bool
 IsSameDimension(ScreenOrientation o1, ScreenOrientation o2)
 {
   bool isO1portrait = (o1 == eScreenOrientation_PortraitPrimary || o1 == eScreenOrientation_PortraitSecondary);
   bool isO2portrait = (o2 == eScreenOrientation_PortraitPrimary || o2 == eScreenOrientation_PortraitSecondary);
   return !(isO1portrait ^ isO2portrait);
@@ -358,17 +349,17 @@ AsyncCompositionManager::ApplyAsyncConte
                                                  &treeTransform,
                                                  scrollOffset);
 
     const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform();
     const FrameMetrics& metrics = container->GetFrameMetrics();
     gfx::Rect displayPortLayersPixels(metrics.mCriticalDisplayPort.IsEmpty() ?
                                       metrics.mDisplayPort : metrics.mCriticalDisplayPort);
     gfx::Margin fixedLayerMargins(0, 0, 0, 0);
-    gfx::Point offset(0, 0);
+    ScreenPoint offset(0, 0);
     SyncFrameMetrics(scrollOffset, treeTransform.mScale.width, metrics.mScrollableRect,
                      mLayersUpdated, displayPortLayersPixels, 1 / rootTransform.GetXScale(),
                      mIsFirstPaint, fixedLayerMargins, offset);
 
     mIsFirstPaint = false;
     mLayersUpdated = false;
 
     // Apply the render offset
@@ -408,108 +399,102 @@ AsyncCompositionManager::TransformScroll
 
   const FrameMetrics& metrics = container->GetFrameMetrics();
   // We must apply the resolution scale before a pan/zoom transform, so we call
   // GetTransform here.
   const gfx3DMatrix& currentTransform = aLayer->GetTransform();
 
   gfx3DMatrix treeTransform;
 
-  float rootScaleX = aRootTransform.GetXScale(),
-        rootScaleY = aRootTransform.GetYScale();
-  // The ratio of layers pixels to device pixels.  The Java
-  // compositor wants to see values in units of device pixels, so we
-  // map our FrameMetrics values to that space.  This is not exposed
-  // as a FrameMetrics helper because it's a deprecated conversion.
-  float devPixelRatioX = 1 / rootScaleX, devPixelRatioY = 1 / rootScaleY;
+  float layerPixelRatioX = 1 / aRootTransform.GetXScale(),
+        layerPixelRatioY = 1 / aRootTransform.GetYScale();
 
-  gfxPoint scrollOffsetLayersPixels(metrics.GetScrollOffsetInLayerPixels());
-  nsIntPoint scrollOffsetDevPixels(
-    NS_lround(scrollOffsetLayersPixels.x * devPixelRatioX),
-    NS_lround(scrollOffsetLayersPixels.y * devPixelRatioY));
+  LayerIntPoint scrollOffsetLayerPixels = LayerIntPoint::FromCSSPointRounded(
+    metrics.mScrollOffset, layerPixelRatioX, layerPixelRatioY);
 
   if (mIsFirstPaint) {
     mContentRect = metrics.mContentRect;
-    SetFirstPaintViewport(scrollOffsetDevPixels,
-                          1/rootScaleX,
+    SetFirstPaintViewport(scrollOffsetLayerPixels,
+                          layerPixelRatioX,
                           mContentRect,
                           metrics.mScrollableRect);
     mIsFirstPaint = false;
   } else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) {
     mContentRect = metrics.mContentRect;
     SetPageRect(metrics.mScrollableRect);
   }
 
   // We synchronise the viewport information with Java after sending the above
   // notifications, so that Java can take these into account in its response.
   // Calculate the absolute display port to send to Java
-  gfx::Rect displayPortLayersPixels(metrics.mCriticalDisplayPort.IsEmpty() ?
-                                    metrics.mDisplayPort : metrics.mCriticalDisplayPort);
-  nsIntRect displayPortDevPixels(
-    NS_lround(displayPortLayersPixels.x * devPixelRatioX),
-    NS_lround(displayPortLayersPixels.y * devPixelRatioY),
-    NS_lround(displayPortLayersPixels.width * devPixelRatioX),
-    NS_lround(displayPortLayersPixels.height * devPixelRatioY));
+  LayerIntRect displayPort = LayerIntRect::FromCSSRectRounded(
+    CSSRect::FromUnknownRect(metrics.mCriticalDisplayPort.IsEmpty()
+                             ? metrics.mDisplayPort
+                             : metrics.mCriticalDisplayPort),
+    layerPixelRatioX, layerPixelRatioY);
 
-  displayPortDevPixels.x += scrollOffsetDevPixels.x;
-  displayPortDevPixels.y += scrollOffsetDevPixels.y;
+  displayPort.x += scrollOffsetLayerPixels.x;
+  displayPort.y += scrollOffsetLayerPixels.y;
 
   gfx::Margin fixedLayerMargins(0, 0, 0, 0);
-  gfx::Point offset(0, 0);
-  SyncViewportInfo(displayPortDevPixels, 1/rootScaleX, mLayersUpdated,
-                   mScrollOffset, mXScale, mYScale, fixedLayerMargins,
+  ScreenPoint offset(0, 0);
+  ScreenPoint scrollOffset(0, 0);
+  float scaleX = 1.0,
+        scaleY = 1.0;
+  SyncViewportInfo(displayPort, layerPixelRatioX, mLayersUpdated,
+                   scrollOffset, scaleX, scaleY, fixedLayerMargins,
                    offset);
   mLayersUpdated = false;
 
   // Apply the render offset
   mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
   // Handle transformations for asynchronous panning and zooming. We determine the
   // zoom used by Gecko from the transformation set on the root layer, and we
   // determine the scroll offset used by Gecko from the frame metrics of the
   // primary scrollable layer. We compare this to the desired zoom and scroll
   // offset in the view transform we obtained from Java in order to compute the
   // transformation we need to apply.
-  float tempScaleDiffX = rootScaleX * mXScale;
-  float tempScaleDiffY = rootScaleY * mYScale;
+  float tempScaleDiffX = aRootTransform.GetXScale() * scaleX;
+  float tempScaleDiffY = aRootTransform.GetYScale() * scaleY;
 
-  nsIntPoint metricsScrollOffset(0, 0);
+  LayerIntPoint metricsScrollOffset(0, 0);
   if (metrics.IsScrollable()) {
-    metricsScrollOffset = scrollOffsetDevPixels;
+    metricsScrollOffset = scrollOffsetLayerPixels;
   }
 
   nsIntPoint scrollCompensation(
-    (mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale,
-    (mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale);
+    (scrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * scaleX,
+    (scrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * scaleY);
   treeTransform = gfx3DMatrix(ViewTransform(-scrollCompensation,
-                                            gfxSize(mXScale, mYScale)));
+                                            gfxSize(scaleX, scaleY)));
 
   // Translate fixed position layers so that they stay in the correct position
-  // when mScrollOffset and metricsScrollOffset differ.
+  // when scrollOffset and metricsScrollOffset differ.
   gfxPoint fixedOffset;
   gfxSize scaleDiff;
 
   // If the contents can fit entirely within the widget area on a particular
   // dimenson, we need to translate and scale so that the fixed layers remain
   // within the page boundaries.
   if (mContentRect.width * tempScaleDiffX < metrics.mCompositionBounds.width) {
     fixedOffset.x = -metricsScrollOffset.x;
     scaleDiff.width = std::min(1.0f, metrics.mCompositionBounds.width / (float)mContentRect.width);
   } else {
-    fixedOffset.x = clamped(mScrollOffset.x / tempScaleDiffX, (float)mContentRect.x,
+    fixedOffset.x = clamped(scrollOffset.x / tempScaleDiffX, (float)mContentRect.x,
                        mContentRect.XMost() - metrics.mCompositionBounds.width / tempScaleDiffX) -
                metricsScrollOffset.x;
     scaleDiff.width = tempScaleDiffX;
   }
 
   if (mContentRect.height * tempScaleDiffY < metrics.mCompositionBounds.height) {
     fixedOffset.y = -metricsScrollOffset.y;
     scaleDiff.height = std::min(1.0f, metrics.mCompositionBounds.height / (float)mContentRect.height);
   } else {
-    fixedOffset.y = clamped(mScrollOffset.y / tempScaleDiffY, (float)mContentRect.y,
+    fixedOffset.y = clamped(scrollOffset.y / tempScaleDiffY, (float)mContentRect.y,
                        mContentRect.YMost() - metrics.mCompositionBounds.height / tempScaleDiffY) -
                metricsScrollOffset.y;
     scaleDiff.height = tempScaleDiffY;
   }
 
   // The transform already takes the resolution scale into account.  Since we
   // will apply the resolution scale again when computing the effective
   // transform, we must apply the inverse resolution scale here.
@@ -562,64 +547,64 @@ AsyncCompositionManager::TransformShadow
       }
     }
   }
 
   return wantNextFrame;
 }
 
 void
-AsyncCompositionManager::SetFirstPaintViewport(const nsIntPoint& aOffset,
+AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
                                                float aZoom,
-                                               const nsIntRect& aPageRect,
-                                               const gfx::Rect& aCssPageRect)
+                                               const LayerIntRect& aPageRect,
+                                               const CSSRect& aCssPageRect)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aPageRect, aCssPageRect);
 #endif
 }
 
 void
-AsyncCompositionManager::SetPageRect(const gfx::Rect& aCssPageRect)
+AsyncCompositionManager::SetPageRect(const CSSRect& aCssPageRect)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SetPageRect(aCssPageRect);
 #endif
 }
 
 void
-AsyncCompositionManager::SyncViewportInfo(const nsIntRect& aDisplayPort,
+AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort,
                                           float aDisplayResolution,
                                           bool aLayersUpdated,
-                                          nsIntPoint& aScrollOffset,
+                                          ScreenPoint& aScrollOffset,
                                           float& aScaleX, float& aScaleY,
                                           gfx::Margin& aFixedLayerMargins,
-                                          gfx::Point& aOffset)
+                                          ScreenPoint& aOffset)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort,
                                             aDisplayResolution,
                                             aLayersUpdated,
                                             aScrollOffset,
                                             aScaleX, aScaleY,
                                             aFixedLayerMargins,
                                             aOffset);
 #endif
 }
 
 void
 AsyncCompositionManager::SyncFrameMetrics(const gfx::Point& aScrollOffset,
                                           float aZoom,
-                                          const gfx::Rect& aCssPageRect,
+                                          const CSSRect& aCssPageRect,
                                           bool aLayersUpdated,
                                           const gfx::Rect& aDisplayPort,
                                           float aDisplayResolution,
                                           bool aIsFirstPaint,
                                           gfx::Margin& aFixedLayerMargins,
-                                          gfx::Point& aOffset)
+                                          ScreenPoint& aOffset)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
                                             aLayersUpdated, aDisplayPort,
                                             aDisplayResolution, aIsFirstPaint,
                                             aFixedLayerMargins, aOffset);
 #endif
 }
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -52,19 +52,17 @@ struct ViewTransform {
  * short circuit that stuff to directly affect layers as they are composited,
  * for example, off-main thread animation, async video, async pan/zoom.
  */
 class AsyncCompositionManager MOZ_FINAL : public RefCounted<AsyncCompositionManager>
 {
   friend class AutoResolveRefLayers;
 public:
   AsyncCompositionManager(LayerManagerComposite* aManager)
-    : mXScale(1.0)
-    , mYScale(1.0)
-    , mLayerManager(aManager)
+    : mLayerManager(aManager)
     , mIsFirstPaint(false)
     , mLayersUpdated(false)
     , mReadyForCompose(true)
   {
     MOZ_COUNT_CTOR(AsyncCompositionManager);
   }
   ~AsyncCompositionManager()
   {
@@ -103,47 +101,45 @@ public:
 
   // True if the underlying layer tree is ready to be composited.
   bool ReadyForCompose() { return mReadyForCompose; }
 
   // Returns true if the next composition will be the first for a
   // particular document.
   bool IsFirstPaint() { return mIsFirstPaint; }
 
-  void SetTransformation(float aScale, const nsIntPoint& aScrollOffset);
-
 private:
   void TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRootTransform);
   // Return true if an AsyncPanZoomController content transform was
   // applied for |aLayer|.  *aWantNextFrame is set to true if the
   // controller wants another animation frame.
   bool ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer* aLayer,
                                         bool* aWantNextFrame);
 
-  void SetFirstPaintViewport(const nsIntPoint& aOffset,
+  void SetFirstPaintViewport(const LayerIntPoint& aOffset,
                              float aZoom,
-                             const nsIntRect& aPageRect,
-                             const gfx::Rect& aCssPageRect);
-  void SetPageRect(const gfx::Rect& aCssPageRect);
-  void SyncViewportInfo(const nsIntRect& aDisplayPort,
+                             const LayerIntRect& aPageRect,
+                             const CSSRect& aCssPageRect);
+  void SetPageRect(const CSSRect& aCssPageRect);
+  void SyncViewportInfo(const LayerIntRect& aDisplayPort,
                         float aDisplayResolution,
                         bool aLayersUpdated,
-                        nsIntPoint& aScrollOffset,
+                        ScreenPoint& aScrollOffset,
                         float& aScaleX, float& aScaleY,
                         gfx::Margin& aFixedLayerMargins,
-                        gfx::Point& aOffset);
+                        ScreenPoint& aOffset);
   void SyncFrameMetrics(const gfx::Point& aScrollOffset,
                         float aZoom,
-                        const gfx::Rect& aCssPageRect,
+                        const CSSRect& aCssPageRect,
                         bool aLayersUpdated,
                         const gfx::Rect& aDisplayPort,
                         float aDisplayResolution,
                         bool aIsFirstPaint,
                         gfx::Margin& aFixedLayerMargins,
-                        gfx::Point& aOffset);
+                        ScreenPoint& aOffset);
 
   /**
    * Recursively applies the given translation to all top-level fixed position
    * layers that are descendants of the given layer.
    * aScaleDiff is considered to be the scale transformation applied when
    * displaying the layers, and is used to make sure the anchor points of
    * fixed position layers remain in the same position.
    */
@@ -162,20 +158,17 @@ private:
   /**
    * Detaches all referents resolved by ResolveRefLayers.
    * Assumes that mLayerManager->GetRoot() and mTargetConfig have not changed
    * since ResolveRefLayers was called.
    */
   void DetachRefLayers();
 
   TargetConfig mTargetConfig;
-  float mXScale;
-  float mYScale;
-  nsIntPoint mScrollOffset;
-  nsIntRect mContentRect;
+  LayerIntRect mContentRect;
 
   nsRefPtr<LayerManagerComposite> mLayerManager;
   // When this flag is set, the next composition will be the first for a
   // particular document (i.e. the document displayed on the screen will change).
   // This happens when loading a new page or switching tabs. We notify the
   // front-end (e.g. Java on Android) about this so that it take the new page
   // size and zoom into account when providing us with the next view transform.
   bool mIsFirstPaint;
--- a/gfx/layers/composite/ThebesLayerComposite.cpp
+++ b/gfx/layers/composite/ThebesLayerComposite.cpp
@@ -232,33 +232,37 @@ ThebesLayerComposite::GetCompositionBoun
   gfxRect compositionBounds;
   ContainerLayer* scrollableLayer = nullptr;
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     const FrameMetrics& parentMetrics = parent->GetFrameMetrics();
     if (parentMetrics.IsScrollable())
       scrollableLayer = parent;
     if (!parentMetrics.mDisplayPort.IsEmpty() && scrollableLayer) {
       // Get the composition bounds, so as not to waste rendering time.
-      compositionBounds = gfxRect(parentMetrics.mCompositionBounds);
+      compositionBounds = gfxRect(parentMetrics.mCompositionBounds.x,
+                                  parentMetrics.mCompositionBounds.y,
+                                  parentMetrics.mCompositionBounds.width,
+                                  parentMetrics.mCompositionBounds.height);
 
       // Calculate the scale transform applied to the root layer to determine
       // the content resolution.
       Layer* rootLayer = Manager()->GetRoot();
       const gfx3DMatrix& rootTransform = rootLayer->GetTransform();
       float scaleX = rootTransform.GetXScale();
       float scaleY = rootTransform.GetYScale();
 
       // Get the content document bounds, in screen-space.
       const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics();
-      const nsIntSize& contentSize = metrics.mContentRect.Size();
+      const LayerIntSize& contentSize = metrics.mContentRect.Size();
       gfx::Point scrollOffset =
         gfx::Point((metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().width) / scaleX,
                    (metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().height) / scaleY);
-      const nsIntPoint& contentOrigin = metrics.mContentRect.TopLeft() -
-        nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y));
+      const nsIntPoint& contentOrigin = nsIntPoint(
+        metrics.mContentRect.x - NS_lround(scrollOffset.x),
+        metrics.mContentRect.y - NS_lround(scrollOffset.y));
       gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y,
                                     contentSize.width, contentSize.height);
       gfxRect contentBounds = scrollableLayer->GetEffectiveTransform().
         TransformBounds(contentRect);
 
       // Clip the composition bounds to the content bounds
       compositionBounds.IntersectRect(compositionBounds, contentBounds);
       break;
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -69,17 +69,17 @@ public:
   }
 
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) MOZ_OVERRIDE { }
 
   /**
    * Declare an offset to use when rendering layers. This will be ignored when
    * rendering to a target instead of the screen.
    */
-  virtual void SetScreenRenderOffset(const gfx::Point& aOffset) MOZ_OVERRIDE {
+  virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) MOZ_OVERRIDE {
     if (aOffset.x || aOffset.y) {
       NS_RUNTIMEABORT("SetScreenRenderOffset not supported by CompositorD3D11.");
     }
     // If the offset is 0, 0 that's okay.
   }
 
   virtual void DrawQuad(const gfx::Rect &aRect, const gfx::Rect &aClipRect,
                         const EffectChain &aEffectChain,
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -827,28 +827,24 @@ void AsyncPanZoomController::SetComposit
 void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) {
   gfx::Point newOffset(mFrameMetrics.mScrollOffset.x + aOffset.x,
                        mFrameMetrics.mScrollOffset.y + aOffset.y);
   FrameMetrics metrics(mFrameMetrics);
   metrics.mScrollOffset = CSSPoint::FromUnknownPoint(newOffset);
   mFrameMetrics = metrics;
 }
 
-void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
+void AsyncPanZoomController::SetPageRect(const CSSRect& aCSSPageRect) {
   FrameMetrics metrics = mFrameMetrics;
-  gfx::Rect pageSize = aCSSPageRect;
   gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
 
   // The page rect is the css page rect scaled by the current zoom.
-  pageSize.ScaleInverseRoundOut(resolution);
-
   // Round the page rect so we don't get any truncation, then get the nsIntRect
   // from this.
-  metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y,
-                                   pageSize.width, pageSize.height);
+  metrics.mContentRect = LayerRect::FromCSSRectRoundOut(aCSSPageRect, resolution);
   metrics.mScrollableRect = aCSSPageRect;
 
   mFrameMetrics = metrics;
 }
 
 void AsyncPanZoomController::ScaleWithFocus(float aZoom,
                                             const nsIntPoint& aFocus) {
   float zoomFactor = aZoom / mFrameMetrics.mZoom.width;
@@ -913,20 +909,20 @@ const gfx::Rect AsyncPanZoomController::
   // If we don't get an estimated paint duration, we probably don't have any
   // data. In this case, we're dealing with either a stationary frame or a first
   // paint. In either of these cases, we can just assume it'll take 1 second to
   // paint. Getting this correct is not important anyways since it's only really
   // useful when accelerating, which can't be happening at this point.
   double estimatedPaintDuration =
     aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0;
 
-  gfxFloat resolution = CalculateResolution(aFrameMetrics).width;
-  nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds;
-  compositionBounds.ScaleInverseRoundIn(resolution);
-  gfx::Rect scrollableRect = aFrameMetrics.mScrollableRect;
+  gfxSize resolution = CalculateResolution(aFrameMetrics);
+  CSSIntRect compositionBounds = LayerIntRect::ToCSSIntRectRoundIn(
+    aFrameMetrics.mCompositionBounds, resolution);
+  gfx::Rect scrollableRect = aFrameMetrics.mScrollableRect.ToUnknownRect();
 
   // Ensure the scrollableRect is at least as big as the compositionBounds
   // because the scrollableRect can be smaller if the content is not large
   // and the scrollableRect hasn't been updated yet.
   // We move the scrollableRect up because we don't know if we can move it
   // down. i.e. we know that scrollableRect can go back as far as zero.
   // but we don't know how much further ahead it can go.
   if (scrollableRect.width < compositionBounds.width) {
@@ -1005,26 +1001,23 @@ AsyncPanZoomController::CalculateIntrins
 AsyncPanZoomController::CalculateResolution(const FrameMetrics& aMetrics)
 {
   gfxSize intrinsicScale = CalculateIntrinsicScale(aMetrics);
   gfxSize userZoom = aMetrics.mZoom;
   return gfxSize(intrinsicScale.width * userZoom.width,
                  intrinsicScale.height * userZoom.height);
 }
 
-/*static*/ gfx::Rect
+/*static*/ CSSRect
 AsyncPanZoomController::CalculateCompositedRectInCssPixels(const FrameMetrics& aMetrics)
 {
   gfxSize resolution = CalculateResolution(aMetrics);
-  gfx::Rect rect(aMetrics.mCompositionBounds.x,
-                 aMetrics.mCompositionBounds.y,
-                 aMetrics.mCompositionBounds.width,
-                 aMetrics.mCompositionBounds.height);
-  rect.ScaleInverseRoundIn(resolution.width, resolution.height);
-  return rect;
+  CSSIntRect rect = LayerIntRect::ToCSSIntRectRoundIn(
+    aMetrics.mCompositionBounds, resolution);
+  return CSSRect(rect);
 }
 
 void AsyncPanZoomController::SetDPI(int aDPI) {
   mDPI = aDPI;
 }
 
 int AsyncPanZoomController::GetDPI() {
   return mDPI;
@@ -1300,20 +1293,20 @@ void AsyncPanZoomController::NotifyLayer
   }
 }
 
 const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
   mMonitor.AssertCurrentThreadOwns();
   return mFrameMetrics;
 }
 
-void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositionBounds) {
+void AsyncPanZoomController::UpdateCompositionBounds(const LayerIntRect& aCompositionBounds) {
   MonitorAutoLock mon(mMonitor);
 
-  nsIntRect oldCompositionBounds = mFrameMetrics.mCompositionBounds;
+  LayerIntRect oldCompositionBounds = mFrameMetrics.mCompositionBounds;
   mFrameMetrics.mCompositionBounds = aCompositionBounds;
 
   // If the window had 0 dimensions before, or does now, we don't want to
   // repaint or update the zoom since we'll run into rendering issues and/or
   // divide-by-zero. This manifests itself as the screen flashing. If the page
   // has gone out of view, the buffer will be cleared elsewhere anyways.
   if (aCompositionBounds.width && aCompositionBounds.height &&
       oldCompositionBounds.width && oldCompositionBounds.height) {
@@ -1331,38 +1324,38 @@ void AsyncPanZoomController::CancelDefau
   }
 }
 
 void AsyncPanZoomController::DetectScrollableSubframe() {
   mDelayPanning = true;
 }
 
 void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
-  gfx::Rect zoomToRect(gfx::Rect(aRect.x, aRect.y, aRect.width, aRect.height));
+  CSSRect zoomToRect(aRect.x, aRect.y, aRect.width, aRect.height);
 
   SetState(ANIMATING_ZOOM);
 
   {
     MonitorAutoLock mon(mMonitor);
 
-    nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
-    gfx::Rect cssPageRect = mFrameMetrics.mScrollableRect;
+    LayerIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
+    CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
     CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
     gfxSize resolution = CalculateResolution(mFrameMetrics);
     gfxSize currentZoom = mFrameMetrics.mZoom;
     float targetZoom;
     gfxFloat targetResolution;
 
     // The minimum zoom to prevent over-zoom-out.
     // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
     // then the CSS content rect, in layers pixels, will be smaller than the
     // composition bounds. If this happens, we can't fill the target composited
     // area with this frame.
     float localMinZoom;
-    gfx::Rect compositedRect = CalculateCompositedRectInCssPixels(mFrameMetrics);
+    CSSRect compositedRect = CalculateCompositedRectInCssPixels(mFrameMetrics);
     localMinZoom =
       std::max(currentZoom.width / (cssPageRect.width / compositedRect.width),
                currentZoom.height / (cssPageRect.height / compositedRect.height));
     localMinZoom = std::max(localMinZoom, mMinZoom);
 
     if (!zoomToRect.IsEmpty()) {
       // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
       zoomToRect = zoomToRect.Intersect(cssPageRect);
@@ -1373,59 +1366,57 @@ void AsyncPanZoomController::ZoomToRect(
     }
     // 1. If the rect is empty, request received from browserElementScrolling.js
     // 2. currentZoom is equal to mMaxZoom and user still double-tapping it
     // 3. currentZoom is equal to localMinZoom and user still double-tapping it
     // Treat these three cases as a request to zoom out as much as possible.
     if (zoomToRect.IsEmpty() ||
         (currentZoom.width == mMaxZoom && targetZoom >= mMaxZoom) ||
         (currentZoom.width == localMinZoom && targetZoom <= localMinZoom)) {
-      nsIntRect cssCompositionBounds = compositionBounds;
-      cssCompositionBounds.ScaleInverseRoundIn(resolution.width,
-                                               resolution.height);
+      CSSIntRect cssCompositionBounds = LayerIntRect::ToCSSIntRectRoundIn(
+        compositionBounds, resolution);
 
       float y = scrollOffset.y;
       float newHeight =
         cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width;
       float dh = cssCompositionBounds.height - newHeight;
 
-      zoomToRect = gfx::Rect(0.0f,
-                             y + dh/2,
-                             cssPageRect.width,
-                             newHeight);
+      zoomToRect = CSSRect(0.0f,
+                           y + dh/2,
+                           cssPageRect.width,
+                           newHeight);
       zoomToRect = zoomToRect.Intersect(cssPageRect);
       targetResolution =
         std::min(compositionBounds.width / zoomToRect.width,
                  compositionBounds.height / zoomToRect.height);
       targetZoom = float(targetResolution / resolution.width) * currentZoom.width;