Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 04 Sep 2012 21:45:09 -0400
changeset 104246 6705e131aeaa3d507604fd8765bd92edc0c0e433
parent 104245 ce16c39e01fbae9db3fd7ca72a21fbb198deed96 (current diff)
parent 104221 f867845a9956e604bd64e819b0dd14ccd6204705 (diff)
child 104247 7b37d27e4c23e7d6751cf09e5be94763267a3d6f
child 104330 e00f615159f19a1735854a277a7242579f1c3b84
child 111026 06cc6f281e4f4ce1d0a67f439ed5e89d89d8de4f
push id14418
push userryanvm@gmail.com
push dateWed, 05 Sep 2012 02:24:42 +0000
treeherdermozilla-inbound@839b06aaa954 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone18.0a1
first release with
nightly linux32
6705e131aeaa / 18.0a1 / 20120905030555 / files
nightly linux64
6705e131aeaa / 18.0a1 / 20120905030555 / files
nightly mac
6705e131aeaa / 18.0a1 / 20120905030555 / files
nightly win32
6705e131aeaa / 18.0a1 / 20120905030555 / files
nightly win64
6705e131aeaa / 18.0a1 / 20120905030555 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
js/jsd/jsd_atom.c
js/jsd/jsd_high.c
js/jsd/jsd_hook.c
js/jsd/jsd_java.c
js/jsd/jsd_lock.c
js/jsd/jsd_obj.c
js/jsd/jsd_scpt.c
js/jsd/jsd_stak.c
js/jsd/jsd_step.c
js/jsd/jsd_text.c
js/jsd/jsd_val.c
js/jsd/jsdebug.c
js/jsd/jsdstubs.c
mobile/android/components/UpdatePrompt.js
--- a/.gitignore
+++ b/.gitignore
@@ -40,11 +40,8 @@ js/src/tests/results-*.txt
 # Java HTML5 parser classes
 parser/html/java/htmlparser/
 parser/html/java/javaparser/
 
 # Ignore the files and directory that Eclipse IDE creates
 .project
 .cproject
 .settings/
-
-# Python stuff installed at build-time
-*.egg-info/
--- a/.hgignore
+++ b/.hgignore
@@ -40,11 +40,8 @@
 
 # SVN directories
 \.svn/
 
 # Ignore the files and directory that Eclipse IDE creates
 \.project$
 \.cproject$
 \.settings/
-
-# Python stuff installed at build-time
-\.egg-info/
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -150,22 +150,16 @@ nsAccessNode::GetNode() const
 }
 
 nsIDocument*
 nsAccessNode::GetDocumentNode() const
 {
   return mContent ? mContent->OwnerDoc() : nullptr;
 }
 
-bool
-nsAccessNode::IsPrimaryForNode() const
-{
-  return true;
-}
-
 void
 nsAccessNode::Language(nsAString& aLanguage)
 {
   aLanguage.Truncate();
 
   if (!mDoc)
     return;
 
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -92,25 +92,16 @@ public:
   }
 
   /**
    * Return the unique identifier of the accessible.
    */
   void* UniqueID() { return static_cast<void*>(this); }
 
   /**
-   * Return true if the accessible is primary accessible for the given DOM node.
-   *
-   * Accessible hierarchy may be complex for single DOM node, in this case
-   * these accessibles share the same DOM node. The primary accessible "owns"
-   * that DOM node in terms it gets stored in the accessible to node map.
-   */
-  virtual bool IsPrimaryForNode() const;//hello
-
-  /**
    * Interface methods on nsIAccessible shared with ISimpleDOM.
    */
   void Language(nsAString& aLocale);
 
 protected:
   void LastRelease();
 
   nsCOMPtr<nsIContent> mContent;
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -684,16 +684,25 @@ public:
    */
   bool IsDefunct() const { return mFlags & eIsDefunct; }
 
   /**
    * Return true if the accessible is no longer in the document.
    */
   bool IsInDocument() const { return !(mFlags & eIsNotInDocument); }
 
+  /**
+  * Return true if the accessible is primary accessible for the given DOM node.
+  *
+  * Accessible hierarchy may be complex for single DOM node, in this case
+  * these accessibles share the same DOM node. The primary accessible "owns"
+  * that DOM node in terms it gets stored in the accessible to node map.
+  */
+  bool IsPrimaryForNode() const { return !(mFlags & eSharedNode); }
+
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Initializing, cache and tree traverse methods
 
   /**
    * Cache accessible children.
    */
@@ -733,41 +742,42 @@ protected:
     { mFlags = (mFlags & ~kChildrenFlagsMask) | aFlag; }
 
   /**
    * Flags used to describe the state of this accessible.
    * @note keep these flags in sync with ChildrenFlags
    */
   enum StateFlags {
     eIsDefunct = 1 << 2, // accessible is defunct
-    eIsNotInDocument = 1 << 3 // accessible is not in document
+    eIsNotInDocument = 1 << 3, // accessible is not in document
+    eSharedNode = 1 << 4 // accessible shares DOM node from another accessible
   };
 
   /**
    * Flags describing the type of this accessible.
    * @note keep these flags in sync with ChildrenFlags and StateFlags
    */
   enum AccessibleTypes {
-    eApplicationAccessible = 1 << 4,
-    eAutoCompleteAccessible = 1 << 5,
-    eAutoCompletePopupAccessible = 1 << 6,
-    eComboboxAccessible = 1 << 7,
-    eDocAccessible = 1 << 8,
-    eHyperTextAccessible = 1 << 9,
-    eHTMLFileInputAccessible = 1 << 10,
-    eHTMLListItemAccessible = 1 << 11,
-    eImageAccessible = 1 << 12,
-    eImageMapAccessible = 1 << 13,
-    eListControlAccessible = 1 << 14,
-    eMenuButtonAccessible = 1 << 15,
-    eMenuPopupAccessible = 1 << 16,
-    eRootAccessible = 1 << 17,
-    eTextLeafAccessible = 1 << 18,
-    eXULDeckAccessible = 1 << 19,
-    eXULTreeAccessible = 1 << 20
+    eApplicationAccessible = 1 << 5,
+    eAutoCompleteAccessible = 1 << 6,
+    eAutoCompletePopupAccessible = 1 << 7,
+    eComboboxAccessible = 1 << 8,
+    eDocAccessible = 1 << 9,
+    eHyperTextAccessible = 1 << 10,
+    eHTMLFileInputAccessible = 1 << 11,
+    eHTMLListItemAccessible = 1 << 12,
+    eImageAccessible = 1 << 13,
+    eImageMapAccessible = 1 << 14,
+    eListControlAccessible = 1 << 15,
+    eMenuButtonAccessible = 1 << 16,
+    eMenuPopupAccessible = 1 << 17,
+    eRootAccessible = 1 << 18,
+    eTextLeafAccessible = 1 << 19,
+    eXULDeckAccessible = 1 << 20,
+    eXULTreeAccessible = 1 << 21
   };
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
   /**
    * Return ARIA role (helper method).
    */
--- a/accessible/src/generic/ApplicationAccessible.cpp
+++ b/accessible/src/generic/ApplicationAccessible.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/Services.h"
 #include "nsIStringBundle.h"
 
 using namespace mozilla::a11y;
 
 ApplicationAccessible::ApplicationAccessible() :
   AccessibleWrap(nullptr, nullptr)
 {
-  mFlags |= eApplicationAccessible;
+  mFlags |= (eApplicationAccessible | eSharedNode);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED1(ApplicationAccessible, Accessible,
                              nsIAccessibleApplication)
 
@@ -275,21 +275,16 @@ ApplicationAccessible::Init()
 }
 
 void
 ApplicationAccessible::Shutdown()
 {
   mAppInfo = nullptr;
 }
 
-bool
-ApplicationAccessible::IsPrimaryForNode() const
-{
-  return false;
-}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public methods
 
 void
 ApplicationAccessible::ApplyARIAState(uint64_t* aState) const
 {
 }
--- a/accessible/src/generic/ApplicationAccessible.h
+++ b/accessible/src/generic/ApplicationAccessible.h
@@ -58,17 +58,16 @@ public:
   NS_IMETHOD DoAction(uint8_t aIndex);
 
   // nsIAccessibleApplication
   NS_DECL_NSIACCESSIBLEAPPLICATION
 
   // nsAccessNode
   virtual void Init();
   virtual void Shutdown();
-  virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual GroupPos GroupPosition();
   virtual ENameValueFlag Name(nsString& aName);
   virtual void ApplyARIAState(uint64_t* aState) const;
   virtual void Description(nsString& aDescription);
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
--- a/accessible/src/html/HTMLImageMapAccessible.cpp
+++ b/accessible/src/html/HTMLImageMapAccessible.cpp
@@ -147,16 +147,19 @@ HTMLImageMapAccessible::CacheChildren()
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLAreaAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLAreaAccessible::
   HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   HTMLLinkAccessible(aContent, aDoc)
 {
+  // Make HTML area DOM element not accessible. HTML image map accessible			
+  // manages its tree itself.
+  mFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLAreaAccessible: nsIAccessible
 
 nsresult
 HTMLAreaAccessible::GetNameInternal(nsAString& aName)
 {
@@ -179,27 +182,16 @@ HTMLAreaAccessible::Description(nsString
 
   // Still to do - follow IE's standard here
   nsCOMPtr<nsIDOMHTMLAreaElement> area(do_QueryInterface(mContent));
   if (area)
     area->GetShape(aDescription);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// HTMLAreaAccessible: nsAccessNode public
-
-bool
-HTMLAreaAccessible::IsPrimaryForNode() const
-{
-  // Make HTML area DOM element not accessible. HTML image map accessible
-  // manages its tree itself.
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // HTMLAreaAccessible: Accessible public
 
 Accessible*
 HTMLAreaAccessible::ChildAtPoint(int32_t aX, int32_t aY,
                                  EWhichChildAtPoint aWhichChild)
 {
   // Don't walk into area accessibles.
   return this;
--- a/accessible/src/html/HTMLImageMapAccessible.h
+++ b/accessible/src/html/HTMLImageMapAccessible.h
@@ -48,19 +48,16 @@ protected:
  * Accessible for image map areas - must be child of image.
  */
 class HTMLAreaAccessible : public HTMLLinkAccessible
 {
 public:
 
   HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
-  // nsAccessNode
-  virtual bool IsPrimaryForNode() const;
-
   // Accessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild);
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
 
   // HyperLinkAccessible
--- a/accessible/src/html/HTMLListAccessible.cpp
+++ b/accessible/src/html/HTMLListAccessible.cpp
@@ -136,33 +136,33 @@ HTMLLIAccessible::CacheChildren()
 
   // Cache children from subtree.
   AccessibleWrap::CacheChildren();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible
 ////////////////////////////////////////////////////////////////////////////////
+HTMLListBulletAccessible::
+  HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+  LeafAccessible(aContent, aDoc)
+{
+  mFlags |= eSharedNode;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: nsAccessNode
 
 nsIFrame*
 HTMLListBulletAccessible::GetFrame() const
 {
   nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   return blockFrame ? blockFrame->GetBullet() : nullptr;
 }
 
-bool
-HTMLListBulletAccessible::IsPrimaryForNode() const
-{
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: Accessible
 
 ENameValueFlag
 HTMLListBulletAccessible::Name(nsString &aName)
 {
   aName.Truncate();
 
--- a/accessible/src/html/HTMLListAccessible.h
+++ b/accessible/src/html/HTMLListAccessible.h
@@ -70,23 +70,21 @@ private:
 
 
 /**
  * Used for bullet of HTML list item element (for example, HTML li).
  */
 class HTMLListBulletAccessible : public LeafAccessible
 {
 public:
-  HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-    LeafAccessible(aContent, aDoc) { }
+  HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~HTMLListBulletAccessible() { }
 
   // nsAccessNode
   virtual nsIFrame* GetFrame() const;
-  virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual ENameValueFlag Name(nsString& aName);
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
   virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
                             uint32_t aLength = PR_UINT32_MAX);
 
--- a/accessible/src/html/HTMLSelectAccessible.cpp
+++ b/accessible/src/html/HTMLSelectAccessible.cpp
@@ -630,16 +630,17 @@ HTMLComboboxAccessible::SelectedOption()
 // HTMLComboboxListAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLComboboxListAccessible::
   HTMLComboboxListAccessible(nsIAccessible* aParent, nsIContent* aContent,
                              DocAccessible* aDoc) :
   HTMLSelectListAccessible(aContent, aDoc)
 {
+  mFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible: nsAccessNode
 
 nsIFrame*
 HTMLComboboxListAccessible::GetFrame() const
 {
@@ -650,22 +651,16 @@ HTMLComboboxListAccessible::GetFrame() c
     if (comboBox) {
       return comboBox->GetDropDown();
     }
   }
 
   return nullptr;
 }
 
-bool
-HTMLComboboxListAccessible::IsPrimaryForNode() const
-{
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible: Accessible
 
 role
 HTMLComboboxListAccessible::NativeRole()
 {
   return roles::COMBOBOX_LIST;
 }
--- a/accessible/src/html/HTMLSelectAccessible.h
+++ b/accessible/src/html/HTMLSelectAccessible.h
@@ -217,17 +217,16 @@ class HTMLComboboxListAccessible : publi
 public:
 
   HTMLComboboxListAccessible(nsIAccessible* aParent, nsIContent* aContent,
                              DocAccessible* aDoc);
   virtual ~HTMLComboboxListAccessible() {}
 
   // nsAccessNode
   virtual nsIFrame* GetFrame() const;
-  virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
 
   // Widgets
   virtual bool IsActiveWidget() const;
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -698,16 +698,17 @@ XULTreeAccessible::CreateTreeItemAccessi
 XULTreeItemAccessibleBase::
   XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc,
                             Accessible* aParent, nsITreeBoxObject* aTree,
                             nsITreeView* aTreeView, int32_t aRow) :
   AccessibleWrap(aContent, aDoc),
   mTree(aTree), mTreeView(aTreeView), mRow(aRow)
 {
   mParent = aParent;
+  mFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessibleBase: nsISupports implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeItemAccessibleBase)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeItemAccessibleBase,
@@ -891,22 +892,16 @@ XULTreeItemAccessibleBase::Shutdown()
 {
   mTree = nullptr;
   mTreeView = nullptr;
   mRow = -1;
 
   AccessibleWrap::Shutdown();
 }
 
-bool
-XULTreeItemAccessibleBase::IsPrimaryForNode() const
-{
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessibleBase: Accessible public methods
 
 // nsIAccessible::groupPosition
 GroupPos
 XULTreeItemAccessibleBase::GroupPosition()
 {
   GroupPos groupPos;
--- a/accessible/src/xul/XULTreeAccessible.h
+++ b/accessible/src/xul/XULTreeAccessible.h
@@ -149,17 +149,16 @@ public:
   NS_IMETHOD SetSelected(bool aSelect);
   NS_IMETHOD TakeFocus();
 
   NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName);
   NS_IMETHOD DoAction(uint8_t aIndex);
 
   // nsAccessNode
   virtual void Shutdown();
-  virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual GroupPos GroupPosition();
   virtual uint64_t NativeState();
   virtual uint64_t NativeInteractiveState() const;
   virtual int32_t IndexInParent() const;
   virtual Relation RelationByType(uint32_t aType);
   virtual Accessible* FocusedChild();
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -464,16 +464,17 @@ XULTreeGridCellAccessible::
   XULTreeGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc,
                             XULTreeGridRowAccessible* aRowAcc,
                             nsITreeBoxObject* aTree, nsITreeView* aTreeView,
                             int32_t aRow, nsITreeColumn* aColumn) :
   LeafAccessible(aContent, aDoc), xpcAccessibleTableCell(this), mTree(aTree),
   mTreeView(aTreeView), mRow(aRow), mColumn(aColumn)
 {
   mParent = aRowAcc;
+  mFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible: nsISupports implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeGridCellAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeGridCellAccessible,
@@ -789,22 +790,16 @@ XULTreeGridCellAccessible::Init()
   int16_t type;
   mColumn->GetType(&type);
   if (type == nsITreeColumn::TYPE_CHECKBOX)
     mTreeView->GetCellValue(mRow, mColumn, mCachedTextEquiv);
   else
     mTreeView->GetCellText(mRow, mColumn, mCachedTextEquiv);
 }
 
-bool
-XULTreeGridCellAccessible::IsPrimaryForNode() const
-{
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible: Accessible public implementation
 
 nsresult
 XULTreeGridCellAccessible::GetAttributesInternal(nsIPersistentProperties* aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aAttributes);
 
--- a/accessible/src/xul/XULTreeGridAccessible.h
+++ b/accessible/src/xul/XULTreeGridAccessible.h
@@ -146,17 +146,16 @@ public:
   NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName);
   NS_IMETHOD DoAction(uint8_t aIndex);
 
   // nsIAccessibleTableCell
   NS_DECL_OR_FORWARD_NSIACCESSIBLETABLECELL_WITH_XPCACCESSIBLETABLECELL
 
   // nsAccessNode
   virtual void Init();
-  virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual void Shutdown();
   virtual ENameValueFlag Name(nsString& aName);
   virtual Accessible* FocusedChild();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties* aAttributes);
   virtual int32_t IndexInParent() const;
   virtual Relation RelationByType(uint32_t aType);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -698,16 +698,25 @@ window.addEventListener('ContentStart', 
       shell.sendChromeEvent({
         type: 'geolocation-status',
         active: (gGeolocationActiveCount == 1)
       });
     }
 }, "geolocation-device-events", false);
 })();
 
+(function headphonesStatusTracker() {
+  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+    shell.sendChromeEvent({
+      type: 'headphones-status',
+      state: aData
+    });
+}, "headphones-status", false);
+})();
+
 (function recordingStatusTracker() {
   let gRecordingActiveCount = 0;
 
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     let oldCount = gRecordingActiveCount;
     if (aData == "starting") {
       gRecordingActiveCount += 1;
     } else if (aData == "shutdown") {
@@ -718,8 +727,17 @@ window.addEventListener('ContentStart', 
     if (gRecordingActiveCount + oldCount == 1) {
       shell.sendChromeEvent({
         type: 'recording-status',
         active: (gRecordingActiveCount == 1)
       });
     }
 }, "recording-device-events", false);
 })();
+
+(function volumeStateTracker() {
+  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+    shell.sendChromeEvent({
+      type: 'volume-state-changed',
+      active: (aData == 'Shared')
+    });
+}, 'volume-state-changed', false);
+})();
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -109,18 +109,18 @@
 @BINPATH@/platform.ini
 #ifndef XP_OS2
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #else
 @BINPATH@/mozsqlt3@DLL_SUFFIX@
 #endif
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
+#ifndef XP_MACOSX
 @BINPATH@/run-mozilla.sh
-#ifndef XP_MACOSX
 @BINPATH@/mozilla-xremote-client
 #endif
 #endif
 
 ; [Components]
 @BINPATH@/components/components.manifest
 @BINPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
--- a/b2g/installer/removed-files.in
+++ b/b2g/installer/removed-files.in
@@ -1,3 +1,6 @@
 README.txt
 @DLL_PREFIX@mozutils@DLL_SUFFIX@
 jssubloader/
+#ifdef XP_MACOSX
+run-mozilla.sh
+#endif
\ No newline at end of file
--- a/browser/branding/aurora/Makefile.in
+++ b/browser/branding/aurora/Makefile.in
@@ -1,67 +1,11 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DEPTH = @DEPTH@
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = @srcdir@
+DEPTH := @DEPTH@
+topsrcdir := @top_srcdir@
+srcdir := @srcdir@
+VPATH := @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
-
-DIRS = \
-	content \
-	locales \
-	$(NULL)
-
-PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
-
-include $(topsrcdir)/config/rules.mk
-
-WINDOWS_BRANDING_FILES = \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-
-OSX_BRANDING_FILES = \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-
-LINUX_BRANDING_FILES = \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-
-OS2_BRANDING_FILES = \
-	firefox-os2.ico \
-	document-os2.ico \
-	$(NULL)
-
-export::
-	$(NSINSTALL) -D $(DIST)/branding
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-	cp $(addprefix $(srcdir)/, $(WINDOWS_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-	cp $(addprefix $(srcdir)/, $(OSX_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-	cp $(addprefix $(srcdir)/, $(LINUX_BRANDING_FILES)) $(DIST)/branding/
-	$(NSINSTALL) -D $(DIST)/install
-endif
-ifeq ($(OS_ARCH),OS2)
-	cp $(addprefix $(srcdir)/, $(OS2_BRANDING_FILES)) $(DIST)/branding/
-endif
+include $(srcdir)/../branding.mk
new file mode 100644
--- /dev/null
+++ b/browser/branding/branding.mk
@@ -0,0 +1,83 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This .mk file is included by all the branding Makefiles. It defines
+# variables that are common to all.
+
+ifndef top_srcdir
+$(error Must define top_srcdir before including this file)
+endif
+
+ifndef srcdir
+$(error Must define srcdir before including this file)
+endif
+
+DIRS := content locales
+PREF_JS_EXPORTS := $(srcdir)/pref/firefox-branding.js
+
+# These are the lists of branding files per platform. These are shared
+# across all branding setups.
+#
+# If you add files to one branding config, you should define the
+# corresponding variable in the respective Makefile and then include this
+# file.
+#
+# If you remove a file from one branding config, that's not currently
+# supported. You should add support for that in this file somehow.
+# Alternatively, you can just reimplement the logic in this file.
+
+windows_files += \
+  firefox.ico \
+  document.ico \
+  branding.nsi \
+  wizHeader.bmp \
+  wizHeaderRTL.bmp \
+  wizWatermark.bmp \
+  newwindow.ico \
+  newtab.ico \
+  pbmode.ico \
+  $(NULL)
+
+osx_files += \
+  background.png \
+  firefox.icns \
+  disk.icns \
+  document.icns \
+  dsstore \
+  $(NULL)
+
+linux_files += \
+  default16.png \
+  default32.png \
+  default48.png \
+  mozicon128.png \
+  $(NULL)
+
+os2_files += \
+  firefox-os2.ico \
+  document-os2.ico \
+  $(NULL)
+
+BRANDING_DEST := $(DIST)/branding
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+BRANDING_FILES := $(windows_files)
+endif
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+BRANDING_FILES := $(osx_files)
+endif
+ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
+BRANDING_FILES := $(linux_files)
+endif
+ifeq ($(OS_ARCH),OS2)
+BRANDING_FILES := $(os2_files)
+endif
+
+BRANDING_FILES := $(addprefix $(srcdir)/,$(BRANDING_FILES))
+
+ifneq ($(BRANDING_FILES),)
+INSTALL_TARGETS += BRANDING
+endif
+
+include $(topsrcdir)/config/rules.mk
--- a/browser/branding/nightly/Makefile.in
+++ b/browser/branding/nightly/Makefile.in
@@ -3,65 +3,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
-
-DIRS = \
-	content \
-	locales \
-	$(NULL)
-
-PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
-
-include $(topsrcdir)/config/rules.mk
-
-WINDOWS_BRANDING_FILES = \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-
-OSX_BRANDING_FILES = \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-
-LINUX_BRANDING_FILES = \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-
-OS2_BRANDING_FILES = \
-	firefox-os2.ico \
-	document-os2.ico \
-	$(NULL)
-
-export::
-	$(NSINSTALL) -D $(DIST)/branding
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-	cp $(addprefix $(srcdir)/, $(WINDOWS_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-	cp $(addprefix $(srcdir)/, $(OSX_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-	cp $(addprefix $(srcdir)/, $(LINUX_BRANDING_FILES)) $(DIST)/branding/
-	$(NSINSTALL) -D $(DIST)/install
-endif
-ifeq ($(OS_ARCH),OS2)
-	cp $(addprefix $(srcdir)/, $(OS2_BRANDING_FILES)) $(DIST)/branding/
-endif
+include $(srcdir)/../branding.mk
--- a/browser/branding/official/Makefile.in
+++ b/browser/branding/official/Makefile.in
@@ -3,65 +3,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
-
-DIRS = \
-	content \
-	locales \
-	$(NULL)
-
-PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
-
-include $(topsrcdir)/config/rules.mk
-
-WINDOWS_BRANDING_FILES = \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-
-OSX_BRANDING_FILES = \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-
-LINUX_BRANDING_FILES = \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-
-OS2_BRANDING_FILES = \
-	firefox-os2.ico \
-	document-os2.ico \
-	$(NULL)
-
-export::
-	$(NSINSTALL) -D $(DIST)/branding
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-	cp $(addprefix $(srcdir)/, $(WINDOWS_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-	cp $(addprefix $(srcdir)/, $(OSX_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-	cp $(addprefix $(srcdir)/, $(LINUX_BRANDING_FILES)) $(DIST)/branding/
-	$(NSINSTALL) -D $(DIST)/install
-endif
-ifeq ($(OS_ARCH),OS2)
-	cp $(addprefix $(srcdir)/, $(OS2_BRANDING_FILES)) $(DIST)/branding/
-endif
+include $(srcdir)/../branding.mk
--- a/browser/branding/unofficial/Makefile.in
+++ b/browser/branding/unofficial/Makefile.in
@@ -3,65 +3,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
-
-DIRS = \
-	content \
-	locales \
-	$(NULL)
-
-PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
-
-include $(topsrcdir)/config/rules.mk
-
-WINDOWS_BRANDING_FILES = \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-
-OSX_BRANDING_FILES = \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-
-LINUX_BRANDING_FILES = \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-
-OS2_BRANDING_FILES = \
-	firefox-os2.ico \
-	document-os2.ico \
-	$(NULL)
-
-export::
-	$(NSINSTALL) -D $(DIST)/branding
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-	cp $(addprefix $(srcdir)/, $(WINDOWS_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-	cp $(addprefix $(srcdir)/, $(OSX_BRANDING_FILES)) $(DIST)/branding/
-endif
-ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-	cp $(addprefix $(srcdir)/, $(LINUX_BRANDING_FILES)) $(DIST)/branding/
-	$(NSINSTALL) -D $(DIST)/install
-endif
-ifeq ($(OS_ARCH),OS2)
-	cp $(addprefix $(srcdir)/, $(OS2_BRANDING_FILES)) $(DIST)/branding/
-endif
+include $(srcdir)/../branding.mk
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -119,18 +119,18 @@
 #ifndef MOZ_NATIVE_SQLITE
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #endif
 #else
 @BINPATH@/mozsqlt3@DLL_SUFFIX@
 #endif
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
+#ifndef XP_MACOSX
 @BINPATH@/run-mozilla.sh
-#ifndef XP_MACOSX
 @BINPATH@/mozilla-xremote-client
 #endif
 #endif
 
 ; [Components]
 @BINPATH@/components/components.manifest
 @BINPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -284,16 +284,19 @@ res/loading-image.png
 res/maccharset.properties
 res/mathml.css
 res/os2charset.properties
 res/quirk.css
 res/ua.css
 res/unixcharset.properties
 res/viewsource.css
 res/wincharset.properties
+#ifdef XP_MACOSX
+run-mozilla.sh
+#endif
 searchplugins/DRAE.gif
 searchplugins/DRAE.png
 searchplugins/DRAE.src
 searchplugins/MediaDICO-fr.gif
 searchplugins/MediaDICO-fr.png
 searchplugins/MediaDICO-fr.src
 searchplugins/allegro-pl.gif
 searchplugins/allegro-pl.png
--- a/build/virtualenv/packages.txt
+++ b/build/virtualenv/packages.txt
@@ -1,12 +1,12 @@
-setup.py:python/simplejson-2.1.1:develop
-setup.py:testing/mozbase/manifestdestiny:develop
-setup.py:testing/mozbase/mozinfo:develop
-setup.py:testing/mozbase/mozinstall:develop
-setup.py:testing/mozbase/mozlog:develop
-setup.py:testing/mozbase/mozprocess:develop
-setup.py:testing/mozbase/mozprofile:develop
-setup.py:testing/mozbase/mozrunner:develop
-setup.py:python/blessings:develop
-setup.py:python/mozbuild:develop
+simplejson.pth:python/simplejson-2.1.1
+manifestdestiny.pth:testing/mozbase/manifestdestiny
+mozinfo.pth:testing/mozbase/mozinfo
+mozinstall.pth:testing/mozbase/mozinstall
+mozlog.pth:testing/mozbase/mozlog
+mozprocess.pth:testing/mozbase/mozprocess
+mozprofile.pth:testing/mozbase/mozprofile
+mozrunner.pth:testing/mozbase/mozrunner
+blessings.pth:python/blessings
+mozbuild.pth:python/mozbuild
 mozilla.pth:build
 mozilla.pth:config
--- a/configure.in
+++ b/configure.in
@@ -186,17 +186,17 @@ if test -n "$gonkdir" ; then
     arm)
         ARCH_DIR=arch-arm
         ;;
     i?86)
         ARCH_DIR=arch-x86
         ;;
     esac
 
-    CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera"
+    CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS"
     dnl Add -llog by default, since we use it all over the place.
     LIBS="$LIBS -llog $STLPORT_LIBS"
 
     LDFLAGS="-mandroid -L$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib -Wl,-rpath-link=$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib --sysroot=$gonkdir/out/target/product/$GONK_PRODUCT/obj/ $LDFLAGS"
 
     dnl prevent cross compile section from using these flags as host flags
@@ -1392,25 +1392,25 @@ if test "$GNU_CC"; then
     AC_MSG_CHECKING([for -z noexecstack option to ld])
     _SAVE_LDFLAGS=$LDFLAGS
     LDFLAGS="$LDFLAGS -Wl,-z,noexecstack"
     AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
                   AC_MSG_RESULT([no])
                   LDFLAGS=$_SAVE_LDFLAGS)
 
     # Check for -mssse3 on $CC
-    AC_MSG_CHECKING([for -mssse3 option to $CC])
-    HAVE_COMPILER_FLAG_MSSSE3=
+    AC_MSG_CHECKING([if toolchain supports -mssse3 option])
+    HAVE_TOOLCHAIN_SUPPORT_MSSSE3=
     _SAVE_CFLAGS=$CFLAGS
     CFLAGS="$CFLAGS -mssse3"
-    AC_TRY_COMPILE(,,AC_MSG_RESULT([yes])
-                     [HAVE_COMPILER_FLAG_MSSSE3=1],
+    AC_TRY_COMPILE([asm ("pmaddubsw %xmm2,%xmm3");],,AC_MSG_RESULT([yes])
+                     [HAVE_TOOLCHAIN_SUPPORT_MSSSE3=1],
                      AC_MSG_RESULT([no]))
     CFLAGS=$_SAVE_CFLAGS
-    AC_SUBST(HAVE_COMPILER_FLAG_MSSSE3)
+    AC_SUBST(HAVE_TOOLCHAIN_SUPPORT_MSSSE3)
 
     # Turn on GNU-specific warnings:
     # -Wall - turn on a lot of warnings
     # -pedantic - this is turned on below
     # -Wpointer-arith - enabled with -pedantic, but good to have even if not
     # -Wdeclaration-after-statement - MSVC doesn't like these
     # -Werror=return-type - catches missing returns, zero false positives
     # -Wtype-limits - catches overflow bugs, few false positives
@@ -3928,21 +3928,17 @@ dnl ======================
 dnl Detect yasm
 dnl ======================
 
 AC_MSG_CHECKING([for YASM assembler])
 AC_CHECK_PROGS(YASM, yasm, "")
 
 if test -n "$YASM"; then
   dnl Pull out yasm's version string
-  changequote(,)
-  _YASM_VER_FILTER='s|.* \([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\).*|\1|p'
-  changequote([,])
-
-  YASM_VERSION=`yasm --version | sed -ne "$_YASM_VER_FILTER"`
+  YASM_VERSION=`yasm --version | $AWK '/^yasm/ { print $2 }'`
   _YASM_MAJOR_VERSION=`echo ${YASM_VERSION} | $AWK -F\. '{ print $1 }'`
   _YASM_MINOR_VERSION=`echo ${YASM_VERSION} | $AWK -F\. '{ print $2 }'`
   _YASM_RELEASE=`      echo ${YASM_VERSION} | $AWK -F\. '{ print $3 }'`
   _YASM_BUILD=`        echo ${YASM_VERSION} | $AWK -F\. '{ print $4 }'`
 fi
 
 if test -z "$SKIP_LIBRARY_CHECKS"; then
 dnl system JPEG support
@@ -5448,16 +5444,19 @@ fi
 
 if test -n "$MOZ_SPEEX_RESAMPLER"; then
     AC_DEFINE(MOZ_SPEEX_RESAMPLER)
 fi
 
 if test -n "$MOZ_CUBEB"; then
     case "$target" in
     *-android*|*-linuxandroid*)
+        if test -n "$gonkdir"; then
+            AC_DEFINE(MOZ_CUBEB)
+        fi
         dnl No Android implementation of libcubeb yet.
         ;;
     *-linux*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
     *-mingw*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/786854.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<table background=""></table>
+</body>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -108,8 +108,9 @@ load 713417.html
 load 713417-2.html
 load 715056.html
 load 741163-1.html
 load 766426.html
 load 771639.html
 load 752226-1.html
 load 752226-2.html
 HTTP(..) load xhr_abortinprogress.html
+load 786854.html
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1984,28 +1984,16 @@ public:
    * element's type.
    *
    * @param aInput the input element to check. NOTE: aInput can't be null.
    * @return whether the input element has autocomplete enabled.
    */
   static bool IsAutocompleteEnabled(nsIDOMHTMLInputElement* aInput);
 
   /**
-   * If the URI is chrome, return true unconditionarlly.
-   *
-   * Otherwise, get the contents of the given pref, and treat it as a
-   * comma-separated list of URIs.  Return true if the given URI's prepath is
-   * in the list, and false otherwise.
-   *
-   * Comparisons are case-insensitive, and whitespace between elements of the
-   * comma-separated list is ignored.
-   */
-  static bool URIIsChromeOrInPref(nsIURI *aURI, const char *aPref);
-
-  /**
    * This will parse aSource, to extract the value of the pseudo attribute
    * with the name specified in aName. See
    * http://www.w3.org/TR/xml-stylesheet/#NT-StyleSheetPI for the specification
    * which is used to parse aSource.
    *
    * @param aSource the string to parse
    * @param aName the name of the attribute to get the value for
    * @param aValue [out] the value for the attribute with name specified in
@@ -2072,34 +2060,16 @@ public:
    *                                  is currently idle or not.   *
    * @return NS_OK                    NS_OK returned if the requested idle service and 
    *                                  the current idle time were successfully obtained.
    *                                  NS_ERROR_FAILURE returned if the the requested
    *                                  idle service or the current idle were not obtained.
    */
   static nsresult IsUserIdle(uint32_t aRequestedIdleTimeInMS, bool* aUserIsIdle);
 
-  /** 
-   * Takes a window and a string to check prefs against. Assumes that
-   * the window is an app window, and that the pref is a comma
-   * seperated list of app urls that have permission to use whatever
-   * the preference refers to (for example, does the current window
-   * have access to mozTelephony). Chrome is always given permissions
-   * for the requested preference. Sets aAllowed based on preference.
-   *
-   * @param aWindow Current window asking for preference permission
-   * @param aPrefURL Preference name
-   * @param aAllowed [out] outparam on whether or not window is allowed
-   *                       to access pref
-   *
-   * @return NS_OK on successful preference lookup, error code otherwise
-   */
-  static nsresult IsOnPrefWhitelist(nsPIDOMWindow* aWindow,
-                                    const char* aPrefURL, bool *aAllowed);
-
   /**
    * Takes a selection, and a text control element (<input> or <textarea>), and
    * returns the offsets in the text content corresponding to the selection.
    * The selection's anchor and focus must both be in the root node passed or a
    * descendant.
    *
    * @param aSelection      Selection to check
    * @param aRoot           Root <input> or <textarea> element
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -1533,39 +1533,40 @@ nsAttrValue::ParseIntMarginValue(const n
     cont->mType = eIntMarginValue;
     SetMiscAtomOrString(&aString);
     return true;
   }
 
   return false;
 }
 
-bool
+void
 nsAttrValue::LoadImage(nsIDocument* aDocument)
 {
   NS_ASSERTION(Type() == eURL, "wrong type");
 
-  nsString val;
-  ToString(val);
-  if (val.IsEmpty()) {
-    return false;
+#ifdef DEBUG
+  {
+    nsString val;
+    ToString(val);
+    NS_ASSERTION(!val.IsEmpty(),
+                 "How did we end up with an empty string for eURL");
   }
+#endif
 
   MiscContainer* cont = GetMiscContainer();
   mozilla::css::URLValue* url = cont->mURL;
   mozilla::css::ImageValue* image = 
     new css::ImageValue(url->GetURI(), url->mString, url->mReferrer,
                         url->mOriginPrincipal, aDocument);
 
   NS_ADDREF(image);
   cont->mImage = image;
   NS_RELEASE(url);
   cont->mType = eImage;
-
-  return true;
 }
 
 void
 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
 {
   NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
   NS_ASSERTION(!GetMiscContainer()->mStringBits,
                "Trying to re-set atom or string!");
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -349,19 +349,18 @@ public:
    * @return whether the value could be parsed
    */
   bool ParseIntMarginValue(const nsAString& aString);
 
   /**
    * Convert a URL nsAttrValue to an Image nsAttrValue.
    *
    * @param aDocument the document this nsAttrValue belongs to.
-   * @return whether an image load was attempted
    */
-  bool LoadImage(nsIDocument* aDocument);
+  void LoadImage(nsIDocument* aDocument);
 
   size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
 private:
   // These have to be the same as in ValueType
   enum ValueBaseType {
     eStringBase =    eString,    // 00
     eOtherBase =     0x01,       // 01
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -653,50 +653,16 @@ nsContentUtils::IsAutocompleteEnabled(ns
     }
 
     form->GetAutocomplete(autocomplete);
   }
 
   return autocomplete.EqualsLiteral("on");
 }
 
-bool
-nsContentUtils::URIIsChromeOrInPref(nsIURI *aURI, const char *aPref)
-{
-  if (!aURI) {
-    return false;
-  }
-
-  nsAutoCString scheme;
-  aURI->GetScheme(scheme);
-  if (scheme.EqualsLiteral("chrome")) {
-    return true;
-  }
-
-  nsAutoCString prePathUTF8;
-  aURI->GetPrePath(prePathUTF8);
-  NS_ConvertUTF8toUTF16 prePath(prePathUTF8);
-
-  const nsAdoptingString& whitelist = Preferences::GetString(aPref);
-
-  // This tokenizer also strips off whitespace around tokens, as desired.
-  nsCharSeparatedTokenizer tokenizer(whitelist, ',',
-    nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL);
-
-  while (tokenizer.hasMoreTokens()) {
-    const nsSubstring& whitelistItem = tokenizer.nextToken();
-
-    if (whitelistItem.Equals(prePath, nsCaseInsensitiveStringComparator())) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 #define SKIP_WHITESPACE(iter, end_iter, end_res)                 \
   while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
     ++(iter);                                                    \
   }                                                              \
   if ((iter) == (end_iter)) {                                    \
     return (end_res);                                            \
   }
 
@@ -6957,90 +6923,16 @@ nsContentUtils::JSArrayToAtomArray(JSCon
       return NS_ERROR_OUT_OF_MEMORY;
     }
     aRetVal.AppendObject(a);
   }
   return NS_OK;
 }
 
 // static
-nsresult
-nsContentUtils::IsOnPrefWhitelist(nsPIDOMWindow* aWindow,
-                                  const char* aPrefURL, bool* aAllowed)
-{
-  // Make sure we're dealing with an inner window.
-  nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
-    aWindow :
-    aWindow->GetCurrentInnerWindow();
-  NS_ENSURE_TRUE(innerWindow, NS_ERROR_FAILURE);
-
-  // Make sure we're being called from a window that we have permission to
-  // access.
-  if (!nsContentUtils::CanCallerAccess(innerWindow)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  // Need the document in order to make security decisions.
-  nsCOMPtr<nsIDocument> document =
-    do_QueryInterface(innerWindow->GetExtantDocument());
-  NS_ENSURE_TRUE(document, NS_NOINTERFACE);
-
-  // Do security checks. We assume that chrome is always allowed.
-  if (nsContentUtils::IsSystemPrincipal(document->NodePrincipal())) {
-    *aAllowed = true;
-    return NS_OK;    
-  }
-
-  // We also allow a comma seperated list of pages specified by
-  // preferences.  
-  nsCOMPtr<nsIURI> originalURI;
-  nsresult rv =
-    document->NodePrincipal()->GetURI(getter_AddRefs(originalURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIURI> documentURI;
-  rv = originalURI->Clone(getter_AddRefs(documentURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Strip the query string (if there is one) before comparing.
-  nsCOMPtr<nsIURL> documentURL = do_QueryInterface(documentURI);
-  if (documentURL) {
-    rv = documentURL->SetQuery(EmptyCString());
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  bool allowed = false;
-
-  // The pref may not exist but in that case we deny access just as we do if
-  // the url doesn't match.
-  nsCString whitelist;
-  if (NS_SUCCEEDED(Preferences::GetCString(aPrefURL,
-                                           &whitelist))) {
-    nsCOMPtr<nsIIOService> ios = do_GetIOService();
-    NS_ENSURE_TRUE(ios, NS_ERROR_FAILURE);
-
-    nsCCharSeparatedTokenizer tokenizer(whitelist, ',');
-    while (tokenizer.hasMoreTokens()) {
-      nsCOMPtr<nsIURI> uri;
-      if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), tokenizer.nextToken(),
-                                 nullptr, nullptr, ios))) {
-        rv = documentURI->EqualsExceptRef(uri, &allowed);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (allowed) {
-          break;
-        }
-      }
-    }
-  }
-  *aAllowed = allowed;
-  return NS_OK;
-}
-
-// static
 void
 nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
                                           Element* aRoot,
                                           int32_t& aOutStartOffset,
                                           int32_t& aOutEndOffset)
 {
   MOZ_ASSERT(aSelection && aRoot);
 
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -1408,17 +1408,19 @@ nsCanvasRenderingContext2D::GetImageForm
 
 //
 // nsCanvasRenderingContext2D impl
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
 {
-    NS_IF_ADDREF(*canvas = mCanvasElement);
+    if (mCanvasElement) {
+      NS_IF_ADDREF(*canvas = mCanvasElement->GetOriginalCanvas());
+    }
 
     return NS_OK;
 }
 
 //
 // state
 //
 
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -1119,17 +1119,19 @@ nsCanvasRenderingContext2DAzure::GetSurf
 
 //
 // nsCanvasRenderingContext2DAzure impl
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
 {
-  NS_IF_ADDREF(*canvas = GetCanvas());
+  if (mCanvasElement) {
+    NS_IF_ADDREF(*canvas = mCanvasElement->GetOriginalCanvas());
+  }
 
   return NS_OK;
 }
 
 //
 // state
 //
 
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2499,17 +2499,18 @@ nsEventStateManager::DispatchLegacyMouse
 
   if (!aTargetFrame || *aStatus == nsEventStatus_eConsumeNoDefault) {
     return;
   }
 
   // Ignore mouse wheel transaction for computing legacy mouse wheel
   // events' delta value.
   nsIScrollableFrame* scrollTarget =
-    ComputeScrollTarget(aTargetFrame, aEvent, false);
+    ComputeScrollTarget(aTargetFrame, aEvent,
+                        COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
 
   nsIFrame* scrollFrame = do_QueryFrame(scrollTarget);
   nsPresContext* pc =
     scrollFrame ? scrollFrame->PresContext() : aTargetFrame->PresContext();
 
   // DOM event's delta vales are computed from CSS pixels.
   nsSize scrollAmount = GetScrollAmount(pc, aEvent, scrollTarget);
   nsIntSize scrollAmountInCSSPixels(
@@ -2683,19 +2684,19 @@ nsEventStateManager::SendPixelScrollEven
 
   nsEventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
                               &event, nullptr, aStatus);
 }
 
 nsIScrollableFrame*
 nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
                                          widget::WheelEvent* aEvent,
-                                         bool aForDefaultAction)
+                                         ComputeScrollTargetOptions aOptions)
 {
-  if (aForDefaultAction) {
+  if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) {
     // If the user recently scrolled with the mousewheel, then they probably
     // want to scroll the same view as before instead of the view under the
     // cursor.  nsMouseWheelTransaction tracks the frame currently being
     // scrolled with the mousewheel. We consider the transaction ended when the
     // mouse moves more than "mousewheel.transaction.ignoremovedelay"
     // milliseconds after the last scroll operation, or any time the mouse moves
     // out of the frame, or when more than "mousewheel.transaction.timeout"
     // milliseconds have passed after the last operation, even if the mouse
@@ -2712,37 +2713,42 @@ nsEventStateManager::ComputeScrollTarget
 
   // If the event doesn't cause scroll actually, we cannot find scroll target
   // because we check if the event can cause scroll actually on each found
   // scrollable frame.
   if (!aEvent->deltaX && !aEvent->deltaY) {
     return nullptr;
   }
 
+  bool checkIfScrollableX =
+    aEvent->deltaX && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
+  bool checkIfScrollableY =
+    aEvent->deltaY && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
+
   nsIScrollableFrame* frameToScroll = nullptr;
-  for (nsIFrame* scrollFrame = aTargetFrame; scrollFrame;
-       scrollFrame = GetParentFrameToScroll(scrollFrame)) {
+  nsIFrame* scrollFrame =
+    !(aOptions & START_FROM_PARENT) ? aTargetFrame :
+                                      GetParentFrameToScroll(aTargetFrame);
+  for (; scrollFrame; scrollFrame = GetParentFrameToScroll(scrollFrame)) {
     // Check whether the frame wants to provide us with a scrollable view.
     frameToScroll = scrollFrame->GetScrollTargetFrame();
     if (!frameToScroll) {
       continue;
     }
 
-    // At computing scroll target for legacy mouse events, we should return
-    // first scrollable element even when it's not scrollable to the direction.
-    if (!aForDefaultAction) {
+    if (!checkIfScrollableX && !checkIfScrollableY) {
       return frameToScroll;
     }
 
     nsPresContext::ScrollbarStyles ss = frameToScroll->GetScrollbarStyles();
     bool hiddenForV = (NS_STYLE_OVERFLOW_HIDDEN == ss.mVertical);
     bool hiddenForH = (NS_STYLE_OVERFLOW_HIDDEN == ss.mHorizontal);
     if ((hiddenForV && hiddenForH) ||
-        (aEvent->deltaY && !aEvent->deltaX && hiddenForV) ||
-        (aEvent->deltaX && !aEvent->deltaY && hiddenForH)) {
+        (checkIfScrollableY && !checkIfScrollableX && hiddenForV) ||
+        (checkIfScrollableX && !checkIfScrollableY && hiddenForH)) {
       continue;
     }
 
     // For default action, we should climb up the tree if cannot scroll it
     // by the event actually.
     bool canScroll = CanScrollOn(frameToScroll,
                                  aEvent->deltaX, aEvent->deltaY);
     // Comboboxes need special care.
@@ -2758,18 +2764,19 @@ nsEventStateManager::ComputeScrollTarget
 
     if (canScroll) {
       return frameToScroll;
     }
   }
 
   nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
       aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
-  return newFrame ?
-    ComputeScrollTarget(newFrame, aEvent, aForDefaultAction) : nullptr;
+  aOptions =
+    static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
+  return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
 }
 
 nsSize
 nsEventStateManager::GetScrollAmount(nsPresContext* aPresContext,
                                      widget::WheelEvent* aEvent,
                                      nsIScrollableFrame* aScrollableFrame)
 {
   MOZ_ASSERT(aPresContext);
@@ -2903,52 +2910,62 @@ nsEventStateManager::DoScrollText(nsIScr
     case widget::WheelEvent::SCROLL_SMOOTHLY:
       mode = nsIScrollableFrame::SMOOTH;
       break;
     default:
       MOZ_NOT_REACHED("Invalid scrollType value comes");
       return;
   }
 
-  // XXX When the scroll target came from the wheel transaction manager, there
-  //     may be another scrollable element in its ancestors.  Then, probably we
-  //     shouldn't set overflowDelta even if the event doesn't cause scroll
-  //     actually because the non-zero overflowDelta could cause another action
-  //     such as "back" or "forward" in the history.
-
   nsIntPoint overflow;
   aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
                              nsIScrollableFrame::DEVICE_PIXELS,
                              mode, &overflow, origin);
 
-  if (isDeltaModePixel) {
+  if (!scrollFrameWeak.IsAlive()) {
+    // If the scroll causes changing the layout, we can think that the event
+    // has been completely consumed by the content.  Then, users probably don't
+    // want additional action.
+    aEvent->overflowDeltaX = aEvent->overflowDeltaY = 0;
+  } else if (isDeltaModePixel) {
     aEvent->overflowDeltaX = overflow.x;
     aEvent->overflowDeltaY = overflow.y;
   } else {
     aEvent->overflowDeltaX =
       static_cast<double>(overflow.x) / scrollAmountInDevPixels.width;
     aEvent->overflowDeltaY =
       static_cast<double>(overflow.y) / scrollAmountInDevPixels.height;
   }
 
   // If CSS overflow properties caused not to scroll, the overflowDelta* values
   // should be same as delta* values since they may be used as gesture event by
-  // widget.
-  if (overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
-    aEvent->overflowDeltaX = aEvent->deltaX;
-  }
-  if (overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
-    aEvent->overflowDeltaY = aEvent->deltaY;
+  // widget.  However, if there is another scrollable element in the ancestor
+  // along the axis, probably users don't want the operation to cause
+  // additional action such as moving history.  In such case, overflowDelta
+  // values should stay zero.
+  if (scrollFrameWeak.IsAlive()) {
+    if (aEvent->deltaX &&
+        overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
+        !ComputeScrollTarget(scrollFrame, aEvent,
+                             COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS)) {
+      aEvent->overflowDeltaX = aEvent->deltaX;
+    }
+    if (aEvent->deltaY &&
+        overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
+        !ComputeScrollTarget(scrollFrame, aEvent,
+                             COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS)) {
+      aEvent->overflowDeltaY = aEvent->deltaY;
+    }
   }
 
   NS_ASSERTION(aEvent->overflowDeltaX == 0 ||
-    (aEvent->overflowDeltaX > 0) == (actualDevPixelScrollAmount.x > 0),
+    (aEvent->overflowDeltaX > 0) == (aEvent->deltaX > 0),
     "The sign of overflowDeltaX is different from the scroll direction");
   NS_ASSERTION(aEvent->overflowDeltaY == 0 ||
-    (aEvent->overflowDeltaY > 0) == (actualDevPixelScrollAmount.y > 0),
+    (aEvent->overflowDeltaY > 0) == (aEvent->deltaY > 0),
     "The sign of overflowDeltaY is different from the scroll direction");
 
   WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent);
 }
 
 void
 nsEventStateManager::DecideGestureEvent(nsGestureNotifyEvent* aEvent,
                                         nsIFrame* targetFrame)
@@ -3267,17 +3284,18 @@ nsEventStateManager::PostHandleEvent(nsP
       }
 
       widget::WheelEvent* wheelEvent = static_cast<widget::WheelEvent*>(aEvent);
       switch (WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent)) {
         case WheelPrefs::ACTION_SCROLL: {
           // For scrolling of default action, we should honor the mouse wheel
           // transaction.
           nsIScrollableFrame* scrollTarget =
-            ComputeScrollTarget(aTargetFrame, wheelEvent, true);
+            ComputeScrollTarget(aTargetFrame, wheelEvent,
+                                COMPUTE_DEFAULT_ACTION_TARGET);
           wheelEvent->overflowDeltaX = wheelEvent->deltaX;
           wheelEvent->overflowDeltaY = wheelEvent->deltaY;
           WheelPrefs::GetInstance()->
             CancelApplyingUserPrefsFromOverflowDelta(wheelEvent);
           if (scrollTarget) {
             DoScrollText(scrollTarget, wheelEvent);
           } else {
             nsMouseWheelTransaction::EndTransaction();
@@ -5166,17 +5184,18 @@ nsEventStateManager::DeltaAccumulator::I
     // Records pixel delta values and init lineOrPageDeltaX and
     // lineOrPageDeltaY for wheel events which are caused by pixel only
     // devices.  Ignore mouse wheel transaction for computing this.  The
     // lineOrPageDelta values will be used by dispatching legacy
     // NS_MOUSE_SCROLL_EVENT (DOMMouseScroll) but not be used for scrolling
     // of default action.  The transaction should be used only for the default
     // action.
     nsIScrollableFrame* scrollTarget =
-      aESM->ComputeScrollTarget(aTargetFrame, aEvent, false);
+      aESM->ComputeScrollTarget(aTargetFrame, aEvent,
+                                COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
     nsIFrame* frame = do_QueryFrame(scrollTarget);
     nsPresContext* pc =
       frame ? frame->PresContext() : aTargetFrame->PresContext();
     nsSize scrollAmount = aESM->GetScrollAmount(pc, aEvent, scrollTarget);
     nsIntSize scrollAmountInCSSPixels(
       nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
       nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
 
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -463,25 +463,51 @@ protected:
                             DeltaDirection aDeltaDirection);
 
   /**
    * ComputeScrollTarget() returns the scrollable frame which should be
    * scrolled.
    *
    * @param aTargetFrame        The event target of the wheel event.
    * @param aEvent              The handling mouse wheel event.
-   * @param aForDefaultAction   Whether this uses wheel transaction or not.
-   *                            If true, returns the latest scrolled frame if
-   *                            there is it.  Otherwise, the nearest ancestor
-   *                            scrollable frame from aTargetFrame.
+   * @param aOptions            The options for finding the scroll target.
+   *                            Callers should use COMPUTE_*.
    * @return                    The scrollable frame which should be scrolled.
    */
+  // These flags are used in ComputeScrollTarget(). Callers should use
+  // COMPUTE_*.
+  enum
+  {
+    PREFER_MOUSE_WHEEL_TRANSACTION               = 1,
+    PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS = 2,
+    PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS = 4,
+    START_FROM_PARENT                            = 8
+  };
+  enum ComputeScrollTargetOptions
+  {
+    // At computing scroll target for legacy mouse events, we should return
+    // first scrollable element even when it's not scrollable to the direction.
+    COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET     = 0,
+    // Default action prefers the scrolled element immediately before if it's
+    // still under the mouse cursor.  Otherwise, it prefers the nearest
+    // scrollable ancestor which will be scrolled actually.
+    COMPUTE_DEFAULT_ACTION_TARGET                =
+      (PREFER_MOUSE_WHEEL_TRANSACTION |
+       PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS |
+       PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS),
+    // Look for the nearest scrollable ancestor which can be scrollable with
+    // aEvent.
+    COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS     =
+      (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | START_FROM_PARENT),
+    COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS     =
+      (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | START_FROM_PARENT)
+  };
   nsIScrollableFrame* ComputeScrollTarget(nsIFrame* aTargetFrame,
                                           mozilla::widget::WheelEvent* aEvent,
-                                          bool aForDefaultAction);
+                                          ComputeScrollTargetOptions aOptions);
 
   /**
    * GetScrollAmount() returns the scroll amount in app uints of one line or
    * one page.  If the wheel event scrolls a page, returns the page width and
    * height.  Otherwise, returns line height for both its width and height.
    *
    * @param aScrollableFrame    A frame which will be scrolled by the event.
    *                            The result of ComputeScrollTarget() is
--- a/content/events/test/window_wheel_default_action.html
+++ b/content/events/test/window_wheel_default_action.html
@@ -35,26 +35,28 @@
       Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
       Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
       Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
       Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
       Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     </div>
   </div>
 </div>
+<div id="spacerForBody"></div>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForFocus(runTests, window);
 
 var gScrollableElement = document.getElementById("scrollable");
 var gScrolledElement = document.getElementById("scrolled");
+var gSpacerForBodyElement = document.getElementById("spacerForBody");
 
 function is()
 {
   window.opener.is.apply(window.opener, arguments);
 }
 
 function ok()
 {
@@ -100,544 +102,734 @@ function doTestScroll(aSettings, aCallba
   const kScrollLeft  = 0x04;
   const kScrollRight = 0x08;
 
   const kTests = [
     { description: "Scroll to bottom by pixel scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to bottom by pixel scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to top by pixel scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to top by pixel scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to right by pixel scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to right by pixel scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to left by pixel scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to left by pixel scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to bottom-right by pixel scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollRight },
     { description: "Scroll to bottom-left by pixel scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollLeft },
     { description: "Scroll to top-left by pixel scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollLeft },
     { description: "Scroll to top-right by pixel scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollRight },
     { description: "Not Scroll by pixel scroll for z",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
 
     { description: "Scroll to bottom by line scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to bottom by line scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to top by line scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to top by line scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to right by line scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to right by line scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to left by line scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to left by line scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to bottom-right by line scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollRight },
     { description: "Scroll to bottom-left by line scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollLeft },
     { description: "Scroll to top-left by line scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollLeft },
     { description: "Scroll to top-right by line scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollRight },
     { description: "Not Scroll by line scroll for z",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
 
     { description: "Scroll to bottom by page scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to bottom by page scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to top by page scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to top by page scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to right by page scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to right by page scroll when lineOrPageDelta is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to left by page scroll even if lineOrPageDelta is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to left by page scroll when lineOrPageDelta is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to bottom-right by page scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollRight },
     { description: "Scroll to bottom-left by page scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollLeft },
     { description: "Scroll to top-left by page scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollLeft },
     { description: "Scroll to top-right by page scroll",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollRight },
     { description: "Not Scroll by page scroll for z",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
 
     // special cases.
 
     // momentum scroll should cause scroll even if the action is zoom, but if the default action is none,
     // shouldn't do it.
     { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 1, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is -1, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 1, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is -1, even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to bottom-right by momentum pixel scroll even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollRight },
     { description: "Scroll to bottom-left by momentum pixel scroll even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollLeft },
     { description: "Scroll to top-left by momentum pixel scroll even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollLeft },
     { description: "Scroll to top-right by momentum pixel scroll even if the action is zoom",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollRight },
     { description: "Not Scroll by momentum pixel scroll for z (action is zoom)",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Not Scroll by momentum pixel scroll if default action is none (action is zoom)",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll,
       prepare: function () { SpecialPowers.setIntPref("mousewheel.default.action", 0); },
       cleanup: function () { SpecialPowers.setIntPref("mousewheel.default.action", 1); } },
 
     // momentum scroll should cause scroll even if the action is history, but if the default action is none,
     // shouldn't do it.
     { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to bottom-right by momentum pixel scroll even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollRight },
     { description: "Scroll to bottom-left by momentum pixel scroll even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown | kScrollLeft },
     { description: "Scroll to top-left by momentum pixel scroll even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollLeft },
     { description: "Scroll to top-right by momentum pixel scroll even if the action is history",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp | kScrollRight },
     { description: "Not Scroll by momentum pixel scroll for z (action is history)",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Not Scroll by momentum pixel scroll if default action is none (action is history)",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
                lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll,
       prepare: function () { SpecialPowers.setIntPref("mousewheel.default.action", 0); },
       cleanup: function () { SpecialPowers.setIntPref("mousewheel.default.action", 1); } },
 
-    // Don't scroll around axis whose overflow style is hidden.
+    // Don't scroll along axis whose overflow style is hidden.
     { description: "Scroll to only bottom by oblique pixel wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown,
       prepare: function() { gScrollableElement.style.overflowX = "hidden"; } },
     { description: "Scroll to only bottom by oblique line wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to only bottom by oblique page wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollDown },
     { description: "Scroll to only top by oblique pixel wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to only top by oblique line wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp },
     { description: "Scroll to only top by oblique page wheel event with overflow-x: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollUp,
       cleanup: function () { gScrollableElement.style.overflowX = "auto"; } },
     { description: "Scroll to only right by oblique pixel wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight,
       prepare: function() { gScrollableElement.style.overflowY = "hidden"; } },
     { description: "Scroll to only right by oblique line wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to only right by oblique page wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollRight },
     { description: "Scroll to only left by oblique pixel wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to only top by oblique line wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft },
     { description: "Scroll to only top by oblique page wheel event with overflow-y: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kScrollLeft,
       cleanup: function () { gScrollableElement.style.overflowY = "auto"; } },
     { description: "Don't be scrolled by oblique pixel wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll,
       prepare: function() { gScrollableElement.style.overflow = "hidden"; } },
     { description: "Don't be scrolled by oblique line wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Don't be scrolled by oblique page wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Don't be scrolled by oblique pixel wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Don't be scrolled by oblique line wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll },
     { description: "Don't be scrolled by oblique page wheel event with overflow: hidden",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
                lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
       expected: kNoScroll,
       cleanup: function () { gScrollableElement.style.overflow = "auto"; } },
+
+    // Don't scroll along axis whose overflow style is hidden and overflow delta values should
+    // be zero if there is ancestor scrollable element.
+    { description: "Scroll to only bottom by oblique pixel wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+               deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollDown,
+      prepare: function() {
+        gScrollableElement.style.overflowX = "hidden";
+        gScrollableElement.style.position = "fixed";
+        gScrollableElement.style.top = "30px";
+        gScrollableElement.style.left = "30px";
+        // Make body element scrollable.
+        gSpacerForBodyElement.style.width = "5000px";
+        gSpacerForBodyElement.style.height = "5000px";
+        document.documentElement.scrollTop = 500;
+        document.documentElement.scrollLeft = 500;
+      } },
+    { description: "Scroll to only bottom by oblique line wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollDown },
+    { description: "Scroll to only bottom by oblique page wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+               deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollDown },
+    { description: "Scroll to only top by oblique pixel wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+               deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollUp },
+    { description: "Scroll to only top by oblique line wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollUp },
+    { description: "Scroll to only top by oblique page wheel event with overflow-x: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+               deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollUp,
+      cleanup: function () { gScrollableElement.style.overflowX = "auto"; } },
+    { description: "Scroll to only right by oblique pixel wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+               deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollRight,
+      prepare: function() { gScrollableElement.style.overflowY = "hidden"; } },
+    { description: "Scroll to only right by oblique line wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollRight },
+    { description: "Scroll to only right by oblique page wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+               deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollRight },
+    { description: "Scroll to only left by oblique pixel wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+               deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollLeft },
+    { description: "Scroll to only top by oblique line wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollLeft },
+    { description: "Scroll to only top by oblique page wheel event with overflow-y: hidden (body is scrollable)",
+      event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+               deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+               shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
+      expected: kScrollLeft,
+      cleanup: function () {
+        gScrollableElement.style.overflowY = "auto";
+        gScrollableElement.style.position = "static";
+        gSpacerForBodyElement.style.width = "";
+        gSpacerForBodyElement.style.height = "";
+      } },
   ];
 
   var description;
 
   var currentTestIndex = -1;
   var isXReverted = (aSettings.deltaMultiplierX < 0);
   var isYReverted = (aSettings.deltaMultiplierY < 0);
 
@@ -709,149 +901,176 @@ function doTestZoom(aSettings, aCallback
   const kPositive = 0x01;
   const kNegative = 0x02;
   const kUseX     = 0x10;
   const kUseY     = 0x20;
   const kTests = [
     { description: "by vertical/positive pixel event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/positive pixel event when its lineOrPageDeltaY is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseY },
     { description: "by vertical/negative pixel event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/negative pixel event when its lineOrPageDeltaY is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseY },
     { description: "by horizotal/positive pixel event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/positive pixel event when its lineOrPageDeltaX is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseX },
     { description: "by horizotal/negative pixel event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/negative pixel event when its lineOrPageDeltaX is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseX },
     { description: "by z pixel event",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 16.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
 
     { description: "by vertical/positive line event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/positive line event when its lineOrPageDeltaY is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseY },
     { description: "by vertical/negative line event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/negative line event when its lineOrPageDeltaY is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseY },
     { description: "by horizotal/positive line event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/positive line event when its lineOrPageDeltaX is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseX },
     { description: "by horizotal/negative line event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/negative line event when its lineOrPageDeltaX is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseX },
     { description: "by z line event",
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
 
     { description: "by vertical/positive page event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/positive page event when its lineOrPageDeltaY is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseY },
     { description: "by vertical/negative page event when its lineOrPageDeltaY is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by vertical/negative page event when its lineOrPageDeltaY is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseY },
     { description: "by horizotal/positive page event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/positive page event when its lineOrPageDeltaX is 1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kPositive | kUseX },
     { description: "by horizotal/negative page event when its lineOrPageDeltaX is 0",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
     { description: "by horizotal/negative page event when its lineOrPageDeltaX is -1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
-               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNegative | kUseX },
     { description: "by z page event",
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
-               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+               lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+               expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
       expected: kNone },
   ];
 
   var description, currentTest;
   var currentTestIndex = -1;
   var isXReverted = (aSettings.deltaMultiplierX < 0);
   var isYReverted = (aSettings.deltaMultiplierY < 0);
 
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -13,16 +13,18 @@
 #include "nsError.h"
 #include "nsNodeInfoManager.h"
 
 #include "nsICanvasElementExternal.h"
 #include "nsLayoutUtils.h"
 
 class nsICanvasRenderingContextInternal;
 class nsIDOMFile;
+class nsHTMLCanvasPrintState;
+class nsITimerCallback;
 class nsIPropertyBag;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
 class LayerManager;
 }
 }
@@ -163,26 +165,40 @@ protected:
                          nsIVariant* aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
                             bool aForceThebes,
                             nsICanvasRenderingContextInternal **aContext);
+  void CallPrintCallback();
 
   nsString mCurrentContextId;
+  nsCOMPtr<nsIDOMHTMLCanvasElement> mOriginalCanvas;
+  nsCOMPtr<nsIPrintCallback> mPrintCallback;
   nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
+  nsCOMPtr<nsHTMLCanvasPrintState> mPrintState;
   
 public:
   // Record whether this canvas should be write-only or not.
   // We set this when script paints an image from a different origin.
   // We also transitively set it when script paints a canvas which
   // is itself write-only.
   bool                     mWriteOnly;
+
+  bool IsPrintCallbackDone();
+
+  void HandlePrintCallback(nsPresContext::nsPresContextType aType);
+
+  nsresult DispatchPrintCallback(nsITimerCallback* aCallback);
+
+  void ResetPrintCallback();
+
+  nsIDOMHTMLCanvasElement* GetOriginalCanvas();
 };
 
 inline nsISupports*
 GetISupports(nsHTMLCanvasElement* p)
 {
   return static_cast<nsGenericElement*>(p);
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2136,17 +2136,18 @@ nsGenericHTMLElement::ParseAttribute(int
 
 bool
 nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
                                                nsIAtom* aAttribute,
                                                const nsAString& aValue,
                                                nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None &&
-      aAttribute == nsGkAtoms::background) {
+      aAttribute == nsGkAtoms::background &&
+      !aValue.IsEmpty()) {
     // Resolve url to an absolute url
     nsIDocument* doc = OwnerDoc();
     nsCOMPtr<nsIURI> baseURI = GetBaseURI();
     nsCOMPtr<nsIURI> uri;
     nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
         getter_AddRefs(uri), aValue, doc, baseURI);
     if (NS_FAILED(rv)) {
       return false;
@@ -2771,22 +2772,24 @@ nsGenericHTMLElement::MapBackgroundInto(
   nsCSSValue* backImage = aData->ValueForBackgroundImage();
   if (backImage->GetUnit() == eCSSUnit_Null &&
       presContext->UseDocumentColors()) {
     // background
     nsAttrValue* value =
       const_cast<nsAttrValue*>(aAttributes->GetAttr(nsGkAtoms::background));
     // If the value is an image, or it is a URL and we attempted a load,
     // put it in the style tree.
-    if (value &&
-        (value->Type() == nsAttrValue::eImage ||
-         (value->Type() == nsAttrValue::eURL &&
-          value->LoadImage(presContext->Document())))) {
-      nsCSSValueList* list = backImage->SetListValue();
-      list->mValue.SetImageValue(value->GetImageValue());
+    if (value) {
+      if (value->Type() == nsAttrValue::eURL) {
+        value->LoadImage(presContext->Document());
+      }
+      if (value->Type() == nsAttrValue::eImage) {
+        nsCSSValueList* list = backImage->SetListValue();
+        list->mValue.SetImageValue(value->GetImageValue());
+      }
     }
   }
 }
 
 void
 nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes,
                                      nsRuleData* aData)
 {
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -23,51 +23,146 @@
 #include "nsStreamUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsFrameManager.h"
 #include "nsDisplayList.h"
 #include "BasicLayers.h"
 #include "imgIEncoder.h"
+#include "nsITimer.h"
+#include "nsAsyncDOMEvent.h"
 
 #include "nsIWritablePropertyBag2.h"
 
 #define DEFAULT_CANVAS_WIDTH 300
 #define DEFAULT_CANVAS_HEIGHT 150
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
+class nsHTMLCanvasPrintState : public nsIDOMMozCanvasPrintState
+{
+public:
+  nsHTMLCanvasPrintState(nsHTMLCanvasElement* aCanvas,
+                         nsICanvasRenderingContextInternal* aContext,
+                         nsITimerCallback* aCallback)
+    : mIsDone(false), mPendingNotify(false), mCanvas(aCanvas),
+      mContext(aContext), mCallback(aCallback)
+  {
+  }
+
+  NS_IMETHOD GetContext(nsISupports** aContext)
+  {
+    NS_ADDREF(*aContext = mContext);
+    return NS_OK;
+  }
+
+  NS_IMETHOD Done()
+  {
+    if (!mPendingNotify && !mIsDone) {
+      // The canvas needs to be invalidated for printing reftests on linux to
+      // work.
+      if (mCanvas) {
+        mCanvas->InvalidateCanvas();
+      }
+      nsRefPtr<nsRunnableMethod<nsHTMLCanvasPrintState> > doneEvent =
+        NS_NewRunnableMethod(this, &nsHTMLCanvasPrintState::NotifyDone);
+      if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
+        mPendingNotify = true;
+      }
+    }
+    return NS_OK;
+  }
+
+  void NotifyDone()
+  {
+    mIsDone = true;
+    mPendingNotify = false;
+    if (mCallback) {
+      mCallback->Notify(nullptr);
+    }
+  }
+
+  bool mIsDone;
+
+  // CC
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsHTMLCanvasPrintState)
+private:
+  virtual ~nsHTMLCanvasPrintState()
+  {
+  }
+  bool mPendingNotify;
+
+protected:
+  nsRefPtr<nsHTMLCanvasElement> mCanvas;
+  nsCOMPtr<nsICanvasRenderingContextInternal> mContext;
+  nsCOMPtr<nsITimerCallback> mCallback;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHTMLCanvasPrintState)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHTMLCanvasPrintState)
+
+DOMCI_DATA(MozCanvasPrintState, nsHTMLCanvasPrintState)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHTMLCanvasPrintState)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozCanvasPrintState)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCanvasPrintState)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLCanvasPrintState)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHTMLCanvasPrintState)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCanvas, nsIDOMHTMLCanvasElement)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCallback)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHTMLCanvasPrintState)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvas)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCallback)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+// ---------------------------------------------------------------------------
+
 nsGenericHTMLElement*
 NS_NewHTMLCanvasElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                         FromParser aFromParser)
 {
   return new nsHTMLCanvasElement(aNodeInfo);
 }
 
 nsHTMLCanvasElement::nsHTMLCanvasElement(already_AddRefed<nsINodeInfo> aNodeInfo)
-  : nsGenericHTMLElement(aNodeInfo), mWriteOnly(false)
+  : nsGenericHTMLElement(aNodeInfo), 
+    mWriteOnly(false)
 {
 }
 
 nsHTMLCanvasElement::~nsHTMLCanvasElement()
 {
+  ResetPrintCallback();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLCanvasElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLCanvasElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrintCallback)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrintState)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalCanvas)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLCanvasElement,
                                                 nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCurrentContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrintCallback)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrintState)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginalCanvas)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(nsHTMLCanvasElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsHTMLCanvasElement, nsGenericElement)
 
 DOMCI_NODE_DATA(HTMLCanvasElement, nsHTMLCanvasElement)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLCanvasElement)
@@ -117,28 +212,99 @@ nsHTMLCanvasElement::SetAttr(int32_t aNa
   {
     rv = UpdateContext();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return rv;
 }
 
+void
+nsHTMLCanvasElement::HandlePrintCallback(nsPresContext::nsPresContextType aType)
+{
+  // Only call the print callback here if 1) we're in a print testing mode or
+  // print preview mode, 2) the canvas has a print callback and 3) the callback
+  // hasn't already been called. For real printing the callback is handled in
+  // nsSimplePageSequenceFrame::PrePrintNextPage.
+  nsCOMPtr<nsIPrintCallback> printCallback;
+  if ((aType == nsPresContext::eContext_PageLayout ||
+       aType == nsPresContext::eContext_PrintPreview) &&
+      !mPrintState &&
+      NS_SUCCEEDED(GetMozPrintCallback(getter_AddRefs(printCallback))) && printCallback) {
+    DispatchPrintCallback(nullptr);
+  }
+}
+
+nsresult
+nsHTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback)
+{
+  // For print reftests the context may not be initialized yet, so get a context
+  // so mCurrentContext is set.
+  if (!mCurrentContext) {
+    nsresult rv;
+    nsCOMPtr<nsISupports> context;
+    rv = GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID,
+                    getter_AddRefs(context));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  mPrintState = new nsHTMLCanvasPrintState(this, mCurrentContext, aCallback);
+
+  nsRefPtr<nsRunnableMethod<nsHTMLCanvasElement> > renderEvent =
+        NS_NewRunnableMethod(this, &nsHTMLCanvasElement::CallPrintCallback);
+  return NS_DispatchToCurrentThread(renderEvent);
+}
+
+void
+nsHTMLCanvasElement::CallPrintCallback()
+{
+  nsCOMPtr<nsIPrintCallback> printCallback;
+  GetMozPrintCallback(getter_AddRefs(printCallback));
+  printCallback->Render(mPrintState);
+}
+
+void
+nsHTMLCanvasElement::ResetPrintCallback()
+{
+  if (mPrintState) {
+    mPrintState = nullptr;
+  }
+}
+
+bool
+nsHTMLCanvasElement::IsPrintCallbackDone()
+{
+  if (mPrintState == nullptr) {
+    return true;
+  }
+
+  return mPrintState->mIsDone;
+}
+
+nsIDOMHTMLCanvasElement*
+nsHTMLCanvasElement::GetOriginalCanvas()
+{
+  return mOriginalCanvas ? mOriginalCanvas.get() : this;
+}
+
+
 nsresult
 nsHTMLCanvasElement::CopyInnerTo(nsGenericElement* aDest)
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     nsHTMLCanvasElement* dest = static_cast<nsHTMLCanvasElement*>(aDest);
+    nsHTMLCanvasElement* self = const_cast<nsHTMLCanvasElement*>(this);
+    dest->mOriginalCanvas = self;
+
     nsCOMPtr<nsISupports> cxt;
     dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt));
     nsCOMPtr<nsIDOMCanvasRenderingContext2D> context2d = do_QueryInterface(cxt);
-    if (context2d) {
-      context2d->DrawImage(const_cast<nsHTMLCanvasElement*>(this),
+    if (context2d && !self->mPrintCallback) {
+      context2d->DrawImage(self,
                            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0);
     }
   }
   return rv;
 }
 
 nsChangeHint
 nsHTMLCanvasElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
@@ -212,16 +378,34 @@ nsHTMLCanvasElement::MozFetchAsStream(ns
 
   nsCOMPtr<nsIInputStreamCallback> asyncCallback;
   rv = NS_NewInputStreamReadyEvent(getter_AddRefs(asyncCallback), aCallback, mainThread);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return asyncCallback->OnInputStreamReady(asyncData);
 }
 
+NS_IMETHODIMP
+nsHTMLCanvasElement::SetMozPrintCallback(nsIPrintCallback *aCallback)
+{
+  mPrintCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLCanvasElement::GetMozPrintCallback(nsIPrintCallback** aCallback)
+{
+  if (mOriginalCanvas) {
+    mOriginalCanvas->GetMozPrintCallback(aCallback);
+    return NS_OK;
+  }
+  NS_IF_ADDREF(*aCallback = mPrintCallback);
+  return NS_OK;
+}
+
 nsresult
 nsHTMLCanvasElement::ExtractData(const nsAString& aType,
                                  const nsAString& aOptions,
                                  nsIInputStream** aStream,
                                  bool& aFellBackToPNG)
 {
   // note that if we don't have a current context, the spec says we're
   // supposed to just return transparent black pixels of the canvas
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -293,17 +293,21 @@ static int PrefChanged(const char* aPref
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     if (value.IsEmpty()) {
       gVolumeScale = 1.0;
     } else {
       NS_ConvertUTF16toUTF8 utf8(value);
       gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nullptr));
     }
   } else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
+#ifdef MOZ_WIDGET_GONK
+    bool value = Preferences::GetBool(aPref, false);
+#else
     bool value = Preferences::GetBool(aPref, true);
+#endif
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     gUseCubeb = value;
   } else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
     // Arbitrary default stream latency of 100ms.  The higher this
     // value, the longer stream volume changes will take to become
     // audible.
     uint32_t value = Preferences::GetUint(aPref, 100);
     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
--- a/content/svg/content/src/DOMSVGStringList.cpp
+++ b/content/svg/content/src/DOMSVGStringList.cpp
@@ -180,14 +180,14 @@ DOMSVGStringList::AppendItem(const nsASt
   return InsertItemBefore(newItem, InternalList().Length(), _retval);
 }
 
 SVGStringList &
 DOMSVGStringList::InternalList()
 {
   if (mIsConditionalProcessingAttribute) {
     nsCOMPtr<DOMSVGTests> tests = do_QueryInterface(mElement);
-    return *tests->GetOrCreateStringListAttribute(mAttrEnum);
+    return tests->mStringListAttributes[mAttrEnum];
   }
   return mElement->GetStringListInfo().mStringLists[mAttrEnum];
 }
 
 } // namespace mozilla
--- a/content/svg/content/src/DOMSVGTests.cpp
+++ b/content/svg/content/src/DOMSVGTests.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGTests.h"
 #include "DOMSVGStringList.h"
-#include "nsError.h" // For NS_PROPTABLE_PROP_OVERWRITTEN
 #include "nsSVGFeatures.h"
 #include "nsSVGSwitchElement.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsStyleUtil.h"
 #include "nsSVGUtils.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
@@ -19,43 +18,48 @@ NS_IMPL_ISUPPORTS1(DOMSVGTests, nsIDOMSV
 
 nsIAtom** DOMSVGTests::sStringListNames[3] =
 {
   &nsGkAtoms::requiredFeatures,
   &nsGkAtoms::requiredExtensions,
   &nsGkAtoms::systemLanguage,
 };
 
+DOMSVGTests::DOMSVGTests()
+{
+  mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true);
+}
+
 /* readonly attribute nsIDOMSVGStringList requiredFeatures; */
 NS_IMETHODIMP
 DOMSVGTests::GetRequiredFeatures(nsIDOMSVGStringList * *aRequiredFeatures)
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
   *aRequiredFeatures = DOMSVGStringList::GetDOMWrapper(
-                         GetOrCreateStringListAttribute(FEATURES), element, true, FEATURES).get();
+                         &mStringListAttributes[FEATURES], element, true, FEATURES).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGStringList requiredExtensions; */
 NS_IMETHODIMP
 DOMSVGTests::GetRequiredExtensions(nsIDOMSVGStringList * *aRequiredExtensions)
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
   *aRequiredExtensions = DOMSVGStringList::GetDOMWrapper(
-                           GetOrCreateStringListAttribute(EXTENSIONS), element, true, EXTENSIONS).get();
+                           &mStringListAttributes[EXTENSIONS], element, true, EXTENSIONS).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGStringList systemLanguage; */
 NS_IMETHODIMP
 DOMSVGTests::GetSystemLanguage(nsIDOMSVGStringList * *aSystemLanguage)
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
   *aSystemLanguage = DOMSVGStringList::GetDOMWrapper(
-                       GetOrCreateStringListAttribute(LANGUAGE), element, true, LANGUAGE).get();
+                       &mStringListAttributes[LANGUAGE], element, true, LANGUAGE).get();
   return NS_OK;
 }
 
 /* boolean hasExtension (in DOMString extension); */
 NS_IMETHODIMP
 DOMSVGTests::HasExtension(const nsAString & extension, bool *_retval)
 {
   *_retval = nsSVGFeatures::HasExtension(extension);
@@ -75,29 +79,25 @@ DOMSVGTests::IsConditionalProcessingAttr
 
 int32_t
 DOMSVGTests::GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const
 {
   const nsDefaultStringComparator defaultComparator;
 
   int32_t lowestRank = -1;
 
-  const SVGStringList *languageStringList = GetStringListAttribute(LANGUAGE);
-  if (!languageStringList) {
-    return lowestRank;
-  }
-  for (uint32_t i = 0; i < languageStringList->Length(); i++) {
+  for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) {
     nsCharSeparatedTokenizer languageTokenizer(aAcceptLangs, ',');
     int32_t index = 0;
     while (languageTokenizer.hasMoreTokens()) {
       const nsSubstring &languageToken = languageTokenizer.nextToken();
-      bool exactMatch = (languageToken == (*languageStringList)[i]);
+      bool exactMatch = (languageToken == mStringListAttributes[LANGUAGE][i]);
       bool prefixOnlyMatch =
         !exactMatch &&
-        nsStyleUtil::DashMatchCompare((*languageStringList)[i],
+        nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i],
                                       languageTokenizer.nextToken(),
                                       defaultComparator);
       if (index == 0 && exactMatch) {
         // best possible match
         return 0;
       }
       if ((exactMatch || prefixOnlyMatch) &&
           (lowestRank == -1 || 2 * index + prefixOnlyMatch < lowestRank)) {
@@ -110,82 +110,79 @@ DOMSVGTests::GetBestLanguagePreferenceRa
 }
 
 const nsString * const DOMSVGTests::kIgnoreSystemLanguage = (nsString *) 0x01;
 
 bool
 DOMSVGTests::PassesConditionalProcessingTests(const nsString *aAcceptLangs) const
 {
   // Required Features
-  const SVGStringList *featuresStringList = GetStringListAttribute(FEATURES);
-  if (featuresStringList && featuresStringList->IsExplicitlySet()) {
-    if (featuresStringList->IsEmpty()) {
+  if (mStringListAttributes[FEATURES].IsExplicitlySet()) {
+    if (mStringListAttributes[FEATURES].IsEmpty()) {
       return false;
     }
     nsCOMPtr<nsIContent> content(
       do_QueryInterface(const_cast<DOMSVGTests*>(this)));
 
-    for (uint32_t i = 0; i < featuresStringList->Length(); i++) {
-      if (!nsSVGFeatures::HasFeature(content, (*featuresStringList)[i])) {
+    for (uint32_t i = 0; i < mStringListAttributes[FEATURES].Length(); i++) {
+      if (!nsSVGFeatures::HasFeature(content, mStringListAttributes[FEATURES][i])) {
         return false;
       }
     }
   }
 
   // Required Extensions
   //
   // The requiredExtensions  attribute defines a list of required language
   // extensions. Language extensions are capabilities within a user agent that
   // go beyond the feature set defined in the SVG specification.
   // Each extension is identified by a URI reference.
   // For now, claim that mozilla's SVG implementation supports XHTML and MathML.
-  const SVGStringList *extensionsStringList = GetStringListAttribute(EXTENSIONS);
-  if (extensionsStringList && extensionsStringList->IsExplicitlySet()) {
-    if (extensionsStringList->IsEmpty()) {
+  if (mStringListAttributes[EXTENSIONS].IsExplicitlySet()) {
+    if (mStringListAttributes[EXTENSIONS].IsEmpty()) {
       return false;
     }
-    for (uint32_t i = 0; i < extensionsStringList->Length(); i++) {
-      if (!nsSVGFeatures::HasExtension((*extensionsStringList)[i])) {
+    for (uint32_t i = 0; i < mStringListAttributes[EXTENSIONS].Length(); i++) {
+      if (!nsSVGFeatures::HasExtension(mStringListAttributes[EXTENSIONS][i])) {
         return false;
       }
     }
   }
 
   if (aAcceptLangs == kIgnoreSystemLanguage) {
     return true;
   }
 
   // systemLanguage
   //
   // Evaluates to "true" if one of the languages indicated by user preferences
   // exactly equals one of the languages given in the value of this parameter,
   // or if one of the languages indicated by user preferences exactly equals a
   // prefix of one of the languages given in the value of this parameter such
   // that the first tag character following the prefix is "-".
-  const SVGStringList *languageStringList = GetStringListAttribute(LANGUAGE);
-  if (languageStringList && languageStringList->IsExplicitlySet()) {
-    if (languageStringList->IsEmpty()) {
+  if (mStringListAttributes[LANGUAGE].IsExplicitlySet()) {
+    if (mStringListAttributes[LANGUAGE].IsEmpty()) {
       return false;
     }
 
     // Get our language preferences
     const nsAutoString acceptLangs(aAcceptLangs ? *aAcceptLangs :
       Preferences::GetLocalizedString("intl.accept_languages"));
 
     if (acceptLangs.IsEmpty()) {
       NS_WARNING("no default language specified for systemLanguage conditional test");
       return false;
     }
 
     const nsDefaultStringComparator defaultComparator;
 
-    for (uint32_t i = 0; i < languageStringList->Length(); i++) {
+    for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) {
       nsCharSeparatedTokenizer languageTokenizer(acceptLangs, ',');
       while (languageTokenizer.hasMoreTokens()) {
-        if (nsStyleUtil::DashMatchCompare((*languageStringList)[i],
+        if (nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i],
                                           languageTokenizer.nextToken(),
                                           defaultComparator)) {
           return true;
         }
       }
     }
     return false;
   }
@@ -195,110 +192,51 @@ DOMSVGTests::PassesConditionalProcessing
 
 bool
 DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute,
                                                  const nsAString& aValue,
                                                  nsAttrValue& aResult)
 {
   for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) {
     if (aAttribute == *sStringListNames[i]) {
-      SVGStringList *stringList = GetOrCreateStringListAttribute(i);
-      if (stringList) {
-        nsresult rv = stringList->SetValue(aValue);
-        if (NS_FAILED(rv)) {
-          stringList->Clear();
-        }
+      nsresult rv = mStringListAttributes[i].SetValue(aValue);
+      if (NS_FAILED(rv)) {
+        mStringListAttributes[i].Clear();
       }
       MaybeInvalidate();
       return true;
     }
   }
   return false;
 }
 
 void
 DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute)
 {
   for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) {
     if (aAttribute == *sStringListNames[i]) {
-      SVGStringList *stringList = GetStringListAttribute(i);
-      if (stringList) {
-        // don't destroy the property in case there are tear-offs
-        // referring to it
-        stringList->Clear();
-        MaybeInvalidate();
-      }
+      mStringListAttributes[i].Clear();
+      MaybeInvalidate();
       return;
     }
   }
 }
 
-// Callback function, for freeing uint64_t values stored in property table
-// when the element goes away
-static void
-ReleaseStringListPropertyValue(void*    aObject,       /* unused */
-                               nsIAtom* aPropertyName, /* unused */
-                               void*    aPropertyValue,
-                               void*    aData          /* unused */)
-{
-  SVGStringList* valPtr =
-    static_cast<SVGStringList*>(aPropertyValue);
-  delete valPtr;
-}
-
-SVGStringList*
-DOMSVGTests::GetStringListAttribute(uint8_t aAttrEnum) const
-{
-  nsIAtom *attrName = GetAttrName(aAttrEnum);
-  const nsCOMPtr<nsSVGElement> element =
-    do_QueryInterface(const_cast<DOMSVGTests*>(this));
-
-  return static_cast<SVGStringList*>(element->GetProperty(attrName));
-}
-
-SVGStringList*
-DOMSVGTests::GetOrCreateStringListAttribute(uint8_t aAttrEnum) const
-{
-  SVGStringList* stringListPtr = GetStringListAttribute(aAttrEnum);
-  if (stringListPtr) {
-    return stringListPtr;
-  }
-  nsIAtom *attrName = GetAttrName(aAttrEnum);
-  const nsCOMPtr<nsSVGElement> element =
-    do_QueryInterface(const_cast<DOMSVGTests*>(this));
-
-  stringListPtr = new SVGStringList();
-  stringListPtr->SetIsCommaSeparated(aAttrEnum == LANGUAGE);
-  nsresult rv = element->SetProperty(attrName,
-                                     stringListPtr,
-                                     ReleaseStringListPropertyValue,
-                                     true);
-  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
-                    "Setting property value when it's already set...?"); 
-
-  if (NS_LIKELY(NS_SUCCEEDED(rv))) {
-    return stringListPtr;
-  }
-  // property-insertion failed (e.g. OOM in property-table code)
-  delete stringListPtr;
-  return nullptr;
-}
-
 nsIAtom*
 DOMSVGTests::GetAttrName(uint8_t aAttrEnum) const
 {
   return *sStringListNames[aAttrEnum];
 }
 
 void
 DOMSVGTests::GetAttrValue(uint8_t aAttrEnum, nsAttrValue& aValue) const
 {
   MOZ_ASSERT(aAttrEnum < ArrayLength(sStringListNames),
              "aAttrEnum out of range");
-  aValue.SetTo(*GetOrCreateStringListAttribute(aAttrEnum), nullptr);
+  aValue.SetTo(mStringListAttributes[aAttrEnum], nullptr);
 }
 
 void
 DOMSVGTests::MaybeInvalidate()
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
 
   nsIContent* parent = element->GetFlattenedTreeParent();
--- a/content/svg/content/src/DOMSVGTests.h
+++ b/content/svg/content/src/DOMSVGTests.h
@@ -19,16 +19,17 @@ class DOMSVGStringList;
 }
 
 class DOMSVGTests : public nsIDOMSVGTests
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSVGTESTS
 
+  DOMSVGTests();
   virtual ~DOMSVGTests() {}
 
   friend class mozilla::DOMSVGStringList;
   typedef mozilla::SVGStringList SVGStringList;
 
   /**
    * Compare the language name(s) in a systemLanguage attribute to the
    * user's language preferences, as defined in
@@ -74,20 +75,19 @@ public:
          nsAttrValue& aResult);
 
   /**
    * Unsets a conditional processing attribute.
    */
   void UnsetAttr(const nsIAtom* aAttribute);
 
   nsIAtom* GetAttrName(uint8_t aAttrEnum) const;
-  SVGStringList* GetStringListAttribute(uint8_t aAttrEnum) const;
-  SVGStringList* GetOrCreateStringListAttribute(uint8_t aAttrEnum) const;
   void GetAttrValue(uint8_t aAttrEnum, nsAttrValue &aValue) const;
 
   void MaybeInvalidate();
 
 private:
   enum { FEATURES, EXTENSIONS, LANGUAGE };
+  SVGStringList mStringListAttributes[3];
   static nsIAtom** sStringListNames[3];
 };
 
 #endif // MOZILLA_DOMSVGTESTS_H__
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -7,16 +7,17 @@
 #include "DOMRequest.h"
 
 #include "mozilla/Util.h"
 #include "nsDOMClassInfo.h"
 #include "DOMError.h"
 #include "nsEventDispatcher.h"
 #include "nsDOMEvent.h"
 #include "nsContentUtils.h"
+#include "nsThreadUtils.h"
 
 using mozilla::dom::DOMRequest;
 using mozilla::dom::DOMRequestService;
 
 DOMRequest::DOMRequest(nsIDOMWindow* aWindow)
   : mResult(JSVAL_VOID)
   , mDone(false)
   , mRooted(false)
@@ -216,8 +217,97 @@ NS_IMETHODIMP
 DOMRequestService::FireError(nsIDOMDOMRequest* aRequest,
                              const nsAString& aError)
 {
   NS_ENSURE_STATE(aRequest);
   static_cast<DOMRequest*>(aRequest)->FireError(aError);
 
   return NS_OK;
 }
+
+class FireSuccessAsyncTask : public nsRunnable
+{
+public:
+  FireSuccessAsyncTask(DOMRequest* aRequest,
+                       const jsval& aResult) :
+    mReq(aRequest),
+    mResult(aResult)
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    nsresult rv;
+    nsIScriptContext* sc = mReq->GetContextForEventHandlers(&rv);
+    MOZ_ASSERT(NS_SUCCEEDED(rv) && sc->GetNativeContext());
+
+    JS_AddValueRoot(sc->GetNativeContext(), &mResult);
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    mReq->FireSuccess(mResult);
+    return NS_OK;
+  }
+
+  ~FireSuccessAsyncTask()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    nsresult rv;
+    nsIScriptContext* sc = mReq->GetContextForEventHandlers(&rv);
+    MOZ_ASSERT(NS_SUCCEEDED(rv) && sc->GetNativeContext());
+
+    // We need to build a new request, otherwise we assert since there won't be
+    // a request available yet.
+    JSAutoRequest ar(sc->GetNativeContext());
+    JS_RemoveValueRoot(sc->GetNativeContext(), &mResult);
+  }
+private:
+  nsRefPtr<DOMRequest> mReq;
+  jsval mResult;
+};
+
+class FireErrorAsyncTask : public nsRunnable
+{
+public:
+  FireErrorAsyncTask(DOMRequest* aRequest,
+                     const nsAString& aError) :
+    mReq(aRequest),
+    mError(aError)
+  {
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    mReq->FireError(mError);
+    return NS_OK;
+  }
+private:
+  nsRefPtr<DOMRequest> mReq;
+  nsString mError;
+};
+
+NS_IMETHODIMP
+DOMRequestService::FireSuccessAsync(nsIDOMDOMRequest* aRequest,
+                                    const jsval& aResult)
+{
+  NS_ENSURE_STATE(aRequest);
+  nsCOMPtr<nsIRunnable> asyncTask =
+    new FireSuccessAsyncTask(static_cast<DOMRequest*>(aRequest), aResult);
+  if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) {
+    NS_WARNING("Failed to dispatch to main thread!");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMRequestService::FireErrorAsync(nsIDOMDOMRequest* aRequest,
+                                  const nsAString& aError)
+{
+  NS_ENSURE_STATE(aRequest);
+  nsCOMPtr<nsIRunnable> asyncTask =
+    new FireErrorAsyncTask(static_cast<DOMRequest*>(aRequest), aError);
+  if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) {
+    NS_WARNING("Failed to dispatch to main thread!");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1341,16 +1341,18 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(CanvasGradient, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CanvasPattern, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TextMetrics, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ImageData, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(MozCanvasPrintState, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(SmartCardEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(WindowUtils, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XSLTProcessor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -3921,16 +3923,20 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(TextMetrics, nsIDOMTextMetrics)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMTextMetrics)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(ImageData, nsIDOMImageData)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMImageData)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(MozCanvasPrintState, nsIDOMMozCanvasPrintState)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCanvasPrintState)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(XSLTProcessor, nsIXSLTProcessor)
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessor)
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessorPrivate)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XPathEvaluator, nsIDOMXPathEvaluator)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathEvaluator)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -317,16 +317,17 @@ DOMCI_CLASS(SVGZoomEvent)
 
 // Canvas
 DOMCI_CLASS(HTMLCanvasElement)
 DOMCI_CLASS(CanvasRenderingContext2D)
 DOMCI_CLASS(CanvasGradient)
 DOMCI_CLASS(CanvasPattern)
 DOMCI_CLASS(TextMetrics)
 DOMCI_CLASS(ImageData)
+DOMCI_CLASS(MozCanvasPrintState)
 
 // SmartCard Events
 DOMCI_CLASS(SmartCardEvent)
 
 // WindowUtils
 DOMCI_CLASS(WindowUtils)
 
 // XSLTProcessor
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -59,16 +59,17 @@
 #include "nsIIOService.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "sampler.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsIDOMFileHandle.h"
+#include "nsPrintfCString.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 static bool IsUniversalXPConnectCapable()
 {
@@ -636,17 +637,58 @@ nsDOMWindowUtils::SendWheelEvent(float a
   nsPresContext* presContext = GetPresContext();
   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 
   wheelEvent.refPoint = ToWidgetPoint(aX, aY, offset, presContext);
 
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&wheelEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
+
+  bool failedX = false;
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO) &&
+      wheelEvent.overflowDeltaX != 0) {
+    failedX = true;
+  }
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE) &&
+      wheelEvent.overflowDeltaX <= 0) {
+    failedX = true;
+  }
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE) &&
+      wheelEvent.overflowDeltaX >= 0) {
+    failedX = true;
+  }
+  bool failedY = false;
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO) &&
+      wheelEvent.overflowDeltaY != 0) {
+    failedY = true;
+  }
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE) &&
+      wheelEvent.overflowDeltaY <= 0) {
+    failedY = true;
+  }
+  if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE) &&
+      wheelEvent.overflowDeltaY >= 0) {
+    failedY = true;
+  }
+
+#ifdef DEBUG
+  if (failedX) {
+    nsPrintfCString debugMsg("SendWheelEvent(): unexpected overflowDeltaX: %f",
+                             wheelEvent.overflowDeltaX);
+    NS_WARNING(debugMsg.get());
+  }
+  if (failedY) {
+    nsPrintfCString debugMsg("SendWheelEvent(): unexpected overflowDeltaY: %f",
+                             wheelEvent.overflowDeltaY);
+    NS_WARNING(debugMsg.get());
+  }
+#endif
+
+  return (!failedX && !failedY) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendTouchEvent(const nsAString& aType,
                                  uint32_t *aIdentifiers,
                                  int32_t *aXs,
                                  int32_t *aYs,
                                  uint32_t *aRxs,
--- a/dom/base/nsIDOMDOMRequest.idl
+++ b/dom/base/nsIDOMDOMRequest.idl
@@ -16,16 +16,18 @@ interface nsIDOMDOMRequest : nsIDOMEvent
 
   readonly attribute jsval result;
   readonly attribute nsIDOMDOMError error;
 
   [implicit_jscontext] attribute jsval onsuccess;
   [implicit_jscontext] attribute jsval onerror;
 };
 
-[scriptable, builtinclass, uuid(eebcdf29-f8fa-4c36-bbc7-2146b1cbaf7b)]
+[scriptable, builtinclass, uuid(10996de9-e6f6-4058-97bd-45f1fe065eb5)]
 interface nsIDOMRequestService : nsISupports
 {
   nsIDOMDOMRequest createRequest(in nsIDOMWindow window);
 
   void fireSuccess(in nsIDOMDOMRequest request, in jsval result);
   void fireError(in nsIDOMDOMRequest request, in DOMString error);
+  void fireSuccessAsync(in nsIDOMDOMRequest request, in jsval result);
+  void fireErrorAsync(in nsIDOMDOMRequest request, in DOMString error);
 };
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2754,29 +2754,29 @@ def wrapForType(type, descriptorProvider
     wrap = getWrapTemplateForType(type, descriptorProvider,
                                   templateValues.get('result', 'result'),
                                   templateValues.get('successCode', None),
                                   templateValues.get('isCreator', False))[0]
 
     defaultValues = {'obj': 'obj'}
     return string.Template(wrap).substitute(defaultValues, **templateValues)
 
-def infallibleForAttr(attr, descriptorProvider):
+def infallibleForMember(member, type, descriptorProvider):
     """
     Determine the fallibility of changing a C++ value of IDL type "type" into
     JS for the given attribute. Apart from isCreator, all the defaults are used,
     since the fallbility does not change based on the boolean values,
     and the template will be discarded.
 
     CURRENT ASSUMPTIONS:
         We assume that successCode for wrapping up return values cannot contain
         failure conditions.
     """
-    return getWrapTemplateForType(attr.type, descriptorProvider, 'result', None,\
-                                  memberIsCreator(attr))[1]
+    return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
+                                  memberIsCreator(member))[1]
 
 def typeNeedsCx(type):
     return (type is not None and
             (type.isCallback() or type.isAny() or type.isObject() or
              (type.isUnion() and
               any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes))))
 
 # Returns a tuple consisting of a CGThing containing the type of the return
@@ -3496,39 +3496,52 @@ class CGMemberJITInfo(CGThing):
         protoID = "prototypes::id::%s" % self.descriptor.interface.identifier.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = "true" if infallible else "false"
         return ("\n"
                 "const JSJitInfo %s = {\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,\n"
-                "  %s,  /* isInfallible. Only relevant for getters. */\n"
+                "  %s,  /* isInfallible. False in setters. */\n"
                 "  false  /* isConstant. Only relevant for getters. */\n"
                 "};\n" % (infoName, opName, protoID, depth, failstr))
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
-            getterinfal = getterinfal and infallibleForAttr(self.member, self.descriptor)
+            getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             result = self.defineJitInfo(getterinfo, getter, getterinfal)
             if not self.member.readonly:
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, False)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
             method = ("(JSJitPropertyOp)%s" % self.member.identifier.name)
-            # Method, much like setters, are always fallible
-            result = self.defineJitInfo(methodinfo, method, False)
+
+            # Methods are infallible if they are infallible, have no arguments
+            # to unwrap, and have a return type that's infallible to wrap up for
+            # return.
+            methodInfal = False
+            sigs = self.member.signatures()
+            if len(sigs) == 1:
+                # Don't handle overloading. If there's more than one signature,
+                # one of them must take arguments.
+                sig = sigs[0]
+                if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
+                    # No arguments and infallible return boxing
+                    methodInfal = True
+
+            result = self.defineJitInfo(methodinfo, method, methodInfal)
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
 def getEnumValueName(value):
     # Some enum values can be empty strings.  Others might have weird
     # characters in them.  Deal with the former by returning "_empty",
     # deal with possible name collisions from that by throwing if the
     # enum value is actually "_empty", and throw on any value
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -159,31 +159,35 @@ BluetoothManager::FireEnabledDisabledEve
 BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow) :
   BluetoothPropertyContainer(BluetoothObjectType::TYPE_MANAGER),
   mEnabled(false)
 {
   BindToOwner(aWindow);
   mPath.AssignLiteral("/");
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(this, "mozsettings-changed", false);
+  if (obs) {
+    obs->AddObserver(this, "mozsettings-changed", false);
+  }
 }
 
 BluetoothManager::~BluetoothManager()
 {
   BluetoothService* bs = BluetoothService::Get();
   // We can be null on shutdown, where this might happen
   if (bs) {
     if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(mPath, this))) {
       NS_WARNING("Failed to unregister object with observer!");
     }
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->RemoveObserver(this, "mozsettings-changed");
+  if (obs) {
+    obs->RemoveObserver(this, "mozsettings-changed");
+  }
 }
 
 nsresult
 BluetoothManager::HandleMozsettingChanged(const PRUnichar* aData)
 {
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"bluetooth.enabled","value":true}
   nsresult rv;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -312,16 +312,24 @@ interface nsIDOMWindowUtils : nsISupport
    *                           DOM_DELTA_PIXEL event, nsEventStateManager will
    *                           dispatch NS_MOUSE_SCROLL event for vertical
    *                           scroll.
    * @param aOptions           Set following flags.
    */
    const unsigned long WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE = 0x0001;
    const unsigned long WHEEL_EVENT_CAUSED_BY_MOMENTUM          = 0x0002;
    const unsigned long WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS    = 0x0004;
+   // If any of the following flags is specified this method will throw an
+   // exception in case the relevant overflowDelta has an unexpected value.
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO      = 0x0010;
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE  = 0x0020;
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE  = 0x0040;
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO      = 0x0100;
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE  = 0x0200;
+   const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE  = 0x0400;
    void sendWheelEvent(in float aX,
                        in float aY,
                        in double aDeltaX,
                        in double aDeltaY,
                        in double aDeltaZ,
                        in unsigned long aDeltaMode,
                        in long aModifiers,
                        in long aLineOrPageDeltaX,
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
@@ -19,17 +19,33 @@
  *
  * @status UNDER_DEVELOPMENT
  */
 
 interface nsIDOMFile;
 interface nsIVariant;
 interface nsIInputStreamCallback;
 
-[scriptable, uuid(5929542B-C68E-48AB-84F9-D9642DA39720)]
+[scriptable, builtinclass, uuid(8d5fb8a0-7782-11e1-b0c4-0800200c9a67)]
+interface nsIDOMMozCanvasPrintState : nsISupports
+{
+  // A canvas rendering context.
+  readonly attribute nsISupports context;
+
+  // To be called when rendering to the context is done.
+  void done();
+};
+
+[scriptable, function, uuid(8d5fb8a0-7782-11e1-b0c4-0800200c9a66)]
+interface nsIPrintCallback : nsISupports
+{
+  void render(in nsIDOMMozCanvasPrintState ctx);
+};
+
+[scriptable, uuid(a7062fca-41c6-4520-b777-3bb30fd77273)]
 interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
 {
   attribute unsigned long width;
   attribute unsigned long height;
   attribute boolean mozOpaque;
 
   nsISupports getContext(in DOMString contextId,
                          [optional] in jsval contextOptions);
@@ -51,10 +67,13 @@ interface nsIDOMHTMLCanvasElement : nsID
   // A Mozilla-only extension to get a canvas context backed by double-buffered
   // shared memory. Only privileged callers can call this.
   nsISupports MozGetIPCContext(in DOMString contextId);
 
   // A Mozilla-only extension that returns the canvas' image data as a data
   // stream in the desired image format.
   void mozFetchAsStream(in nsIInputStreamCallback callback,
                                         [optional] in DOMString type);
+
+  // A Mozilla-only callback that is called during the printing process.
+  attribute nsIPrintCallback mozPrintCallback;
 };
 
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -13,16 +13,18 @@
  * limitations under the License.
  */
 
 #include <android/log.h> 
 
 #include "mozilla/Hal.h"
 #include "AudioManager.h"
 #include "gonk/AudioSystem.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
 
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla::hal;
 using namespace mozilla;
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args) 
 
@@ -48,21 +50,37 @@ InternalSetAudioRoutes(SwitchState aStat
 {
   if (aState == SWITCH_STATE_ON) {
     AudioManager::SetAudioRoute(nsIAudioManager::FORCE_HEADPHONES);
   } else if (aState == SWITCH_STATE_OFF) {
     AudioManager::SetAudioRoute(nsIAudioManager::FORCE_SPEAKER);
   }
 }
 
+static void
+NotifyHeadphonesStatus(SwitchState aState)
+{
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    if (aState == SWITCH_STATE_ON) {
+      obs->NotifyObservers(nullptr, "headphones-status", NS_LITERAL_STRING("on").get());
+    } else if (aState == SWITCH_STATE_OFF) {
+      obs->NotifyObservers(nullptr, "headphones-status", NS_LITERAL_STRING("off").get());
+    } else {
+      obs->NotifyObservers(nullptr, "headphones-status", NS_LITERAL_STRING("unknown").get());
+    }
+  }
+}
+
 class HeadphoneSwitchObserver : public SwitchObserver
 {
 public:
   void Notify(const SwitchEvent& aEvent) {
     InternalSetAudioRoutes(aEvent.status());
+    NotifyHeadphonesStatus(aEvent.status());
   }
 };
 
 AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT),
                  mObserver(new HeadphoneSwitchObserver())
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
   
--- a/dom/system/gonk/Makefile.in
+++ b/dom/system/gonk/Makefile.in
@@ -56,16 +56,17 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   AudioManager.cpp \
   AutoMounter.cpp \
   AutoMounterSetting.cpp \
   GonkGPSGeolocationProvider.cpp \
   nsVolume.cpp \
   nsVolumeService.cpp \
   nsVolumeStat.cpp \
+  TimeSetting.cpp \
   Volume.cpp \
   VolumeCommand.cpp \
   VolumeManager.cpp \
   VolumeServiceIOThread.cpp \
   VolumeServiceTest.cpp \
   $(NULL)
 # for our local copy of AudioSystem.h
 LOCAL_INCLUDES += -I$(topsrcdir)/media/libsydneyaudio/src
--- a/dom/system/gonk/SystemWorkerManager.cpp
+++ b/dom/system/gonk/SystemWorkerManager.cpp
@@ -24,23 +24,25 @@
 #include "nsIWorkerHolder.h"
 #include "nsIXPConnect.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/workers/Workers.h"
 #ifdef MOZ_WIDGET_GONK
 #include "mozilla/ipc/Netd.h"
 #include "AutoMounter.h"
+#include "TimeSetting.h"
 #endif
 #include "mozilla/ipc/Ril.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsRadioInterfaceLayer.h"
 #include "WifiWorker.h"
+#include "mozilla/StaticPtr.h"
 
 USING_WORKERS_NAMESPACE
 
 using namespace mozilla::dom::gonk;
 using namespace mozilla::ipc;
 #ifdef MOZ_WIDGET_GONK
 using namespace mozilla::system;
 #endif
@@ -342,16 +344,20 @@ SystemWorkerManager::SystemWorkerManager
 SystemWorkerManager::~SystemWorkerManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gInstance || gInstance == this,
                "There should only be one instance!");
   gInstance = nullptr;
 }
 
+#ifdef MOZ_WIDGET_GONK
+static mozilla::StaticRefPtr<TimeSetting> sTimeSetting;
+#endif
+
 nsresult
 SystemWorkerManager::Init()
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   NS_ASSERTION(NS_IsMainThread(), "We can only initialize on the main thread");
@@ -374,16 +380,17 @@ SystemWorkerManager::Init()
   rv = InitWifi(cx);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize WiFi Networking!");
     return rv;
   }
 
 #ifdef MOZ_WIDGET_GONK
   InitAutoMounter();
+  sTimeSetting = new TimeSetting();
   rv = InitNetd(cx);
   NS_ENSURE_SUCCESS(rv, rv);
 #endif
 
   nsCOMPtr<nsIObserverService> obs =
     do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
   if (!obs) {
     NS_WARNING("Failed to get observer service!");
@@ -403,16 +410,17 @@ void
 SystemWorkerManager::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   mShutdown = true;
 
 #ifdef MOZ_WIDGET_GONK
   ShutdownAutoMounter();
+  sTimeSetting = nullptr;
 #endif
 
   StopRil();
 
   mRIL = nullptr;
 
 #ifdef MOZ_WIDGET_GONK
   StopNetd();
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/TimeSetting.cpp
@@ -0,0 +1,197 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/message_loop.h"
+#include "jsapi.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Services.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsIJSContextStack.h"
+#include "nsIObserverService.h"
+#include "nsISettingsService.h"
+#include "nsJSUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "TimeSetting.h"
+#include "xpcpublic.h"
+
+#undef LOG
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Time Setting" , ## args)
+#define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, "Time Setting" , ## args)
+
+#define TIME_TIMEZONE       "time.timezone"
+#define MOZSETTINGS_CHANGED "mozsettings-changed"
+
+namespace mozilla {
+namespace system {
+
+class InitTimezoneCb MOZ_FINAL : public nsISettingsServiceCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  InitTimezoneCb() {}
+
+  NS_IMETHOD Handle(const nsAString &aName, const JS::Value &aResult, JSContext *aContext) {
+    // If we don't have time.timezone value in the settings, we need
+    // to initialize the settings based on the current system timezone
+    // to make settings consistent with system. This usually happens
+    // at the very first boot. After that, settings must have a value.
+    if (aResult.isNull()) {
+      // Get the current system timezone and convert it to a JS string.
+      nsCString curTimezone = hal::GetTimezone();
+      NS_ConvertUTF8toUTF16 utf16Str(curTimezone);
+      JSString *jsStr = JS_NewUCStringCopyN(aContext, utf16Str.get(), utf16Str.Length());
+
+      // Set the settings based on the current system timezone.
+      nsCOMPtr<nsISettingsServiceLock> lock;
+      nsCOMPtr<nsISettingsService> settingsService =
+        do_GetService("@mozilla.org/settingsService;1");
+      if (!settingsService) {
+        ERR("Failed to get settingsLock service!");
+        return NS_OK;
+      }
+      settingsService->GetLock(getter_AddRefs(lock));
+      lock->Set(TIME_TIMEZONE, STRING_TO_JSVAL(jsStr), nullptr, nullptr);
+      return NS_OK;
+    }
+
+    // Set the system timezone based on the current settings.
+    if (aResult.isString()) {
+      return TimeSetting::SetTimezone(aResult, aContext);
+    }
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD HandleError(const nsAString &aName, JSContext *aContext) {
+    ERR("InitTimezoneCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ISUPPORTS1(InitTimezoneCb, nsISettingsServiceCallback)
+
+TimeSetting::TimeSetting()
+{
+  // Setup an observer to watch changes to the setting.
+  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+  if (!observerService) {
+    ERR("GetObserverService failed");
+    return;
+  }
+  nsresult rv;
+  rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
+  if (NS_FAILED(rv)) {
+    ERR("AddObserver failed");
+    return;
+  }
+
+  // Read the 'time.timezone' setting in order to start with a known
+  // value at boot time. The handle() will be called after reading.
+  nsCOMPtr<nsISettingsServiceLock> lock;
+  nsCOMPtr<nsISettingsService> settingsService =
+    do_GetService("@mozilla.org/settingsService;1");
+  if (!settingsService) {
+    ERR("Failed to get settingsLock service!");
+    return;
+  }
+  settingsService->GetLock(getter_AddRefs(lock));
+  nsCOMPtr<nsISettingsServiceCallback> callback = new InitTimezoneCb();
+  lock->Get(TIME_TIMEZONE, callback);
+}
+
+nsresult TimeSetting::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);
+
+  // 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;
+}
+
+TimeSetting::~TimeSetting()
+{
+  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+  if (observerService) {
+    observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
+  }
+}
+
+NS_IMPL_ISUPPORTS1(TimeSetting, nsIObserver)
+
+NS_IMETHODIMP
+TimeSetting::Observe(nsISupports *aSubject,
+                     const char *aTopic,
+                     const PRUnichar *aData)
+{
+  if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
+    return NS_OK;
+  }
+
+  // Note that this function gets called for any and all settings changes,
+  // so we need to carefully check if we have the one we're interested in.
+  //
+  // The string that we're interested in will be a JSON string that looks like:
+  // {"key":"time.timezone","value":"America/Chicago"}
+
+  // Get the safe JS context.
+  nsCOMPtr<nsIThreadJSContextStack> stack =
+    do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+  if (!stack) {
+    ERR("Failed to get JSContextStack");
+    return NS_OK;
+  }
+  JSContext *cx = stack->GetSafeJSContext();
+  if (!cx) {
+    ERR("Failed to GetSafeJSContext");
+    return NS_OK;
+  }
+
+  // Parse the JSON value.
+  nsDependentString dataStr(aData);
+  JS::Value val;
+  if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+      !val.isObject()) {
+    return NS_OK;
+  }
+
+  // Get the key, which should be the JS string "time.timezone".
+  JSObject &obj(val.toObject());
+  JS::Value key;
+  if (!JS_GetProperty(cx, &obj, "key", &key) ||
+      !key.isString()) {
+    return NS_OK;
+  }
+  JSBool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), TIME_TIMEZONE, &match) ||
+      match != JS_TRUE) {
+    return NS_OK;
+  }
+
+  // Get the value, which should be a JS string like "America/Chicago".
+  JS::Value value;
+  if (!JS_GetProperty(cx, &obj, "value", &value) ||
+      !value.isString()) {
+    return NS_OK;
+  }
+
+  // Set the system timezone.
+  return SetTimezone(value, cx);
+}
+
+} // namespace system
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/TimeSetting.h
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_system_timesetting_h__
+#define mozilla_system_timesetting_h__
+
+#include "jspubtd.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace system {
+
+class ResultListener;
+
+class TimeSetting : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  TimeSetting();
+  virtual ~TimeSetting();
+  static nsresult SetTimezone(const JS::Value &aValue, JSContext *aContext);
+};
+
+} // namespace system
+} // namespace mozilla
+
+#endif // mozilla_system_timesetting_h__
+
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -525,16 +525,17 @@ var interfaceNamesInGlobalScope =
     "SVGFitToViewBox",
     "SVGAElement",
     "NavigatorCamera",
     "CameraControl",
     "CameraCapabilities",
     "CameraManager",
     "CSSSupportsRule",
     "MozMobileCellInfo",
+    "MozCanvasPrintState",
     "TCPSocket"
   ]
 
 for (var i in Components.interfaces) {
   var s = i.toString();
   var name = null;
   if (s.indexOf("nsIDOM") == 0) {
     name = s.substring("nsIDOM".length);
--- a/gfx/skia/Makefile.in
+++ b/gfx/skia/Makefile.in
@@ -365,17 +365,17 @@ endif
 ifneq (,$(INTEL_ARCHITECTURE))
 CPPSRCS += \
 	SkBitmapProcState_opts_SSE2.cpp \
 	SkBlitRect_opts_SSE2.cpp \
 	SkBlitRow_opts_SSE2.cpp \
 	SkUtils_opts_SSE2.cpp \
 	opts_check_SSE2.cpp \
 	$(NULL)
-ifdef HAVE_COMPILER_FLAG_MSSSE3
+ifdef HAVE_TOOLCHAIN_SUPPORT_MSSSE3
 DEFINES += -DSK_BUILD_SSSE3
 CPPSRCS += SkBitmapProcState_opts_SSSE3.cpp
 endif
 else
 ifeq ($(CPU_ARCH)_$(GNU_CC),arm_1)
 CPPSRCS += \
 	SkBitmapProcState_opts_arm.cpp \
 	SkBlitRow_opts_arm.cpp \
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -427,16 +427,23 @@ AdjustSystemClock(int32_t aDeltaMillisec
 
 void 
 SetTimezone(const nsCString& aTimezoneSpec)
 {
   AssertMainThread();
   PROXY_IF_SANDBOXED(SetTimezone(aTimezoneSpec));
 }
 
+nsCString
+GetTimezone()
+{
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetTimezone());
+}
+
 void
 EnableSensorNotifications(SensorType aSensor) {
   AssertMainThread();
   PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
 }
 
 void
 DisableSensorNotifications(SensorType aSensor) {
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -245,16 +245,22 @@ void AdjustSystemClock(int32_t aDeltaMil
 /**
  * Set timezone
  * @param aTimezoneSpec The definition can be found in 
  * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  */
 void SetTimezone(const nsCString& aTimezoneSpec);
 
 /**
+ * Get timezone
+ * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ */
+nsCString GetTimezone();
+
+/**
  * Reboot the device.
  */
 void Reboot();
 
 /**
  * Power off the device.
  */
 void PowerOff();
--- a/hal/fallback/FallbackTime.cpp
+++ b/hal/fallback/FallbackTime.cpp
@@ -13,10 +13,16 @@ namespace hal_impl {
 void 
 AdjustSystemClock(int32_t aDeltaMilliseconds)
 {}
 
 void
 SetTimezone(const nsCString& aTimezoneSpec)
 {}
 
+nsCString
+GetTimezone()
+{
+  return EmptyCString();
+}
+
 } // namespace hal_impl
 } // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -607,16 +607,24 @@ void
 SetTimezone(const nsCString& aTimezoneSpec)
 { 
   property_set("persist.sys.timezone", aTimezoneSpec.get());
   // this function is automatically called by the other time conversion 
   // functions that depend on the timezone. To be safe, we call it manually.  
   tzset();
 }
 
+nsCString 
+GetTimezone()
+{
+  char timezone[32];
+  property_get("persist.sys.timezone", timezone, "");
+  return nsCString(timezone);
+}
+
 // Nothing to do here.  Gonk widgetry always listens for screen
 // orientation changes.
 void
 EnableScreenConfigurationNotifications()
 {
 }
 
 void
--- a/hal/gonk/GonkSensor.cpp
+++ b/hal/gonk/GonkSensor.cpp
@@ -159,24 +159,18 @@ public:
 
 private:
   SensorData mSensorData;
   InfallibleTArray<float> mSensorValues;
 };
 
 namespace hal_impl {
 
-class SensorStatus {
-public:
-  SensorData data;
-  DebugOnly<int> count;
-};
-
 static int sActivatedSensors = 0;
-static SensorStatus sSensorStatus[NUM_SENSOR_TYPE];
+static DebugOnly<int> sSensorRefCount[NUM_SENSOR_TYPE];
 static base::Thread* sSwitchThread;
 
 static void
 PollSensorsOnce()
 {
   if (!sActivatedSensors) {
     return;
   }
@@ -201,31 +195,31 @@ PollSensorsOnce()
   }
 }
 
 static void
 SwitchSensor(bool aActivate, sensor_t aSensor, pthread_t aThreadId)
 {
   int index = HardwareSensorToHalSensor(aSensor.type);
 
-  MOZ_ASSERT(sSensorStatus[index].count || aActivate);
+  MOZ_ASSERT(sSensorRefCount[index] || aActivate);
 
   SensorDevice& device = SensorDevice::getInstance();
 
   device.activate((void*)aThreadId, aSensor.handle, aActivate);
   device.setDelay((void*)aThreadId, aSensor.handle, DEFAULT_DEVICE_POLL_RATE);
 
   if (aActivate) {
     if (++sActivatedSensors == 1) {
       MessageLoop::current()->PostTask(FROM_HERE,
                                        NewRunnableFunction(PollSensorsOnce));
     }
-    sSensorStatus[index].count++;
+    sSensorRefCount[index]++;
   } else {
-    sSensorStatus[index].count--;
+    sSensorRefCount[index]--;
     --sActivatedSensors;
   }
 }
 
 static void
 SetSensorState(SensorType aSensor, bool activate)
 {
   int type = HalSensorToHardwareSensor(aSensor);
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -104,19 +104,21 @@ parent:
     sync GetScreenEnabled() returns (bool enabled);
     SetScreenEnabled(bool enabled);
 
     sync GetCpuSleepAllowed() returns (bool allowed);
     SetCpuSleepAllowed(bool allowed);
 
     sync GetScreenBrightness() returns (double brightness);
     SetScreenBrightness(double brightness);
-    
+
     AdjustSystemClock(int32 aDeltaMilliseconds);
     SetTimezone(nsCString aTimezoneSpec);
+    sync GetTimezone()
+      returns (nsCString aTimezoneSpec);
 
     sync SetLight(LightType light, LightConfiguration aConfig)
       returns (bool status);
     sync GetLight(LightType light)
       returns (LightConfiguration aConfig, bool status);
 
     Reboot();
     PowerOff();
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -190,16 +190,24 @@ AdjustSystemClock(int32_t aDeltaMillisec
 }
 
 void
 SetTimezone(const nsCString& aTimezoneSpec)
 {
   Hal()->SendSetTimezone(nsCString(aTimezoneSpec));
 } 
 
+nsCString
+GetTimezone()
+{
+  nsCString timezone;
+  Hal()->SendGetTimezone(&timezone);
+  return timezone;
+}
+
 void
 Reboot()
 {
   Hal()->SendReboot();
 }
 
 void
 PowerOff()
@@ -527,16 +535,26 @@ public:
     if (!AppProcessHasPermission(this, "systemclock-write")) {
       return false;
     }
     hal::SetTimezone(aTimezoneSpec);
     return true;  
   }
 
   virtual bool
+  RecvGetTimezone(nsCString *aTimezoneSpec) MOZ_OVERRIDE
+  {
+    if (!AppProcessHasPermission(this, "systemclock-read")) {
+      return false;
+    }
+    *aTimezoneSpec = hal::GetTimezone();
+    return true;
+  }
+
+  virtual bool
   RecvReboot() MOZ_OVERRIDE
   {
     if (!AppProcessHasPermission(this, "power")) {
       return false;
     }
     hal::Reboot();
     return true;
   }
--- a/intl/uconv/src/nsTextToSubURI.cpp
+++ b/intl/uconv/src/nsTextToSubURI.cpp
@@ -40,17 +40,17 @@ NS_IMETHODIMP  nsTextToSubURI::ConvertAn
      rv = ccm->GetUnicodeEncoder(charset, &encoder);
      NS_RELEASE(ccm);
      if (NS_SUCCEEDED(rv)) {
        rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, (PRUnichar)'?');
        if(NS_SUCCEEDED(rv))
        {
           char buf[256];
           char *pBuf = buf;
-          int32_t ulen = NS_strlen(text);
+          int32_t ulen = text ? NS_strlen(text) : 0;
           int32_t outlen = 0;
           if(NS_SUCCEEDED(rv = encoder->GetMaxLength(text, ulen, &outlen))) 
           {
              if(outlen >= 256) {
                 pBuf = (char*)NS_Alloc(outlen+1);
              }
              if(nullptr == pBuf) {
                 outlen = 255;
--- a/js/jsd/Makefile.in
+++ b/js/jsd/Makefile.in
@@ -31,28 +31,28 @@ XPCSHELL_TESTS  = test
 # REQUIRES	= java js
 
 EXPORTS		= jsdebug.h
 
 ifdef JS_THREADSAFE
 DEFINES         += -DJS_THREADSAFE
 endif
 
-CSRCS		= \
-		  jsdebug.c \
-		  jsd_atom.c \
-		  jsd_high.c \
-		  jsd_hook.c \
-		  jsd_lock.c \
-		  jsd_obj.c \
-		  jsd_scpt.c \
-		  jsd_stak.c \
-		  jsd_step.c \
-		  jsd_text.c \
-		  jsd_val.c \
+CPPSRCS		+= \
+		  jsdebug.cpp \
+		  jsd_atom.cpp \
+		  jsd_high.cpp \
+		  jsd_hook.cpp \
+		  jsd_lock.cpp \
+		  jsd_obj.cpp \
+		  jsd_scpt.cpp \
+		  jsd_stak.cpp \
+		  jsd_step.cpp \
+		  jsd_text.cpp \
+		  jsd_val.cpp \
 		  $(NULL)
 
 ifdef ENABLE_TESTS
 TOOL_DIRS		+= test
 endif
 
 include $(topsrcdir)/config/rules.mk
 
--- a/js/jsd/jsd.h
+++ b/js/jsd/jsd.h
@@ -23,30 +23,17 @@
 * in other embeddings.
 */
 #ifdef MOZILLA_CLIENT
 #define JSD_THREADSAFE 1
 /* define JSD_HAS_DANGEROUS_THREAD 1 */
 #define JSD_USE_NSPR_LOCKS 1
 #endif /* MOZILLA_CLIENT */
 
-
-/* Get jstypes.h included first. After that we can use PR macros for doing
-*  this extern "C" stuff!
-*/
-#ifdef __cplusplus
-extern "C"
-{
-#endif
 #include "jstypes.h"
-#ifdef __cplusplus
-}
-#endif
-
-JS_BEGIN_EXTERN_C
 #include "jsprf.h"
 #include "jsutil.h" /* Added by JSIFY */
 #include "jshash.h" /* Added by JSIFY */
 #include "jsclist.h"
 #include "jsdebug.h"
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include "jsd_lock.h"
@@ -56,19 +43,16 @@ JS_BEGIN_EXTERN_C
 #include <string.h>
 
 #ifdef LIVEWIRE
 #include <base/pblock.h>
 #include <base/session.h>
 #include <frame/log.h>
 #include <frame/req.h>
 #endif /* LIVEWIRE */
-JS_END_EXTERN_C
-
-JS_BEGIN_EXTERN_C
 
 #define JSD_MAJOR_VERSION 1
 #define JSD_MINOR_VERSION 1
 
 /***************************************************************************/
 /* handy macros */
 #undef  CHECK_BIT_FLAG
 #define CHECK_BIT_FLAG(f,b) ((f)&(b))
@@ -133,21 +117,21 @@ struct JSDContext
     JSCList                 removedSources;
     unsigned                   sourceAlterCount;
     JSHashTable*            atoms;
     JSCList                 objectsList;
     JSHashTable*            objectsTable;
     JSDProfileData*         callingFunctionPData;
     int64_t                 lastReturnTime;
 #ifdef JSD_THREADSAFE
-    void*                   scriptsLock;
-    void*                   sourceTextLock;
-    void*                   objectsLock;
-    void*                   atomsLock;
-    void*                   threadStatesLock;
+    JSDStaticLock*          scriptsLock;
+    JSDStaticLock*          sourceTextLock;
+    JSDStaticLock*          objectsLock;
+    JSDStaticLock*          atomsLock;
+    JSDStaticLock*          threadStatesLock;
 #endif /* JSD_THREADSAFE */
 #ifdef JSD_HAS_DANGEROUS_THREAD
     void*                   dangerousThread;
 #endif /* JSD_HAS_DANGEROUS_THREAD */
 
 };
 
 struct JSDScript
@@ -754,17 +738,17 @@ jsd_SetException(JSDContext* jsdc, JSDTh
  *      jsd_Unlock
  *      jsd_IsLocked
  *      jsd_CurrentThread
  */
 
 #ifdef JSD_THREADSAFE
 
 /* the system-wide lock */
-extern void* _jsd_global_lock;
+extern JSDStaticLock* _jsd_global_lock;
 #define JSD_LOCK()                               \
     JS_BEGIN_MACRO                               \
         if(!_jsd_global_lock)                    \
             _jsd_global_lock = jsd_CreateLock(); \
         JS_ASSERT(_jsd_global_lock);             \
         jsd_Lock(_jsd_global_lock);              \
     JS_END_MACRO
 
@@ -1106,11 +1090,9 @@ jsdlw_AppHookProc(LWDBGApp* app,
                   JSBool created,
                   void *callerdata);
 #endif
 
 
 #endif
 /***************************************************************************/
 
-JS_END_EXTERN_C
-
 #endif /* jsd_h___ */
rename from js/jsd/jsd_atom.c
rename to js/jsd/jsd_atom.cpp
rename from js/jsd/jsd_high.c
rename to js/jsd/jsd_high.cpp
--- a/js/jsd/jsd_high.c
+++ b/js/jsd/jsd_high.cpp
@@ -19,17 +19,17 @@ static JSD_UserCallbacks _callbacks;
 static void*             _user = NULL; 
 static JSRuntime*        _jsrt = NULL;
 
 #ifdef JSD_HAS_DANGEROUS_THREAD
 static void* _dangerousThread = NULL;
 #endif
 
 #ifdef JSD_THREADSAFE
-void* _jsd_global_lock = NULL;
+JSDStaticLock* _jsd_global_lock = NULL;
 #endif
 
 #ifdef DEBUG
 void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
 {
     JS_ASSERT(jsdc->inited);
     JS_ASSERT(jsdc->jsrt);
     JS_ASSERT(jsdc->dumbContext);
@@ -179,17 +179,16 @@ static void
 
 JSDContext*
 jsd_DebuggerOnForUser(JSRuntime*         jsrt, 
                       JSD_UserCallbacks* callbacks, 
                       void*              user,
                       JSObject*          scopeobj)
 {
     JSDContext* jsdc;
-    JSContext* iter = NULL;
 
     jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
     if( ! jsdc )
         return NULL;
 
     /*
      * Set hooks here.  The new/destroy script hooks are on even when
      * the debugger is paused.  The destroy hook so we'll clean up
@@ -279,17 +278,16 @@ jsd_SetUserCallbacks(JSRuntime* jsrt, JS
         memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
     else
         memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
 }
 
 void*
 jsd_SetContextPrivate(JSDContext* jsdc, void *data)
 {
-    void *rval = jsdc->data;
     jsdc->data = data;
     return data;
 }
 
 void*
 jsd_GetContextPrivate(JSDContext* jsdc)
 {
     return jsdc->data;
rename from js/jsd/jsd_hook.c
rename to js/jsd/jsd_hook.cpp
rename from js/jsd/jsd_java.c
rename to js/jsd/jsd_java.cpp
rename from js/jsd/jsd_lock.c
rename to js/jsd/jsd_lock.cpp
--- a/js/jsd/jsd_lock.c
+++ b/js/jsd/jsd_lock.cpp
@@ -12,16 +12,18 @@
 * Otherwise, there are stubs that can be filled in with your own locking     
 * code. Also, note that these stubs include a jsd_CurrentThread()            
 * implementation that only works on Win32 - this is needed for the inprocess 
 * Java-based debugger.                                                       
 */                                                                           
 
 #include "jsd.h"
 
+#include "js/Utility.h"
+
 #ifdef JSD_THREADSAFE
 
 #ifdef JSD_USE_NSPR_LOCKS
 
 #include "prlock.h"
 #include "prthread.h"
 
 #ifdef JSD_ATTACH_THREAD_HACK
@@ -72,22 +74,22 @@ void ASSERT_VALID_LOCK(JSDStaticLock* lo
     JS_ASSERT(lock->lock);
     JS_ASSERT(lock->count >= 0);
     JS_ASSERT(lock->sig == (uint16_t) JSD_LOCK_SIG);
 }    
 #else
 #define ASSERT_VALID_LOCK(x) ((void)0)
 #endif
 
-void*
+JSDStaticLock*
 jsd_CreateLock()
 {
     JSDStaticLock* lock;
 
-    if( ! (lock = calloc(1, sizeof(JSDStaticLock))) || 
+    if( ! (lock = js_pod_calloc<JSDStaticLock>(1)) ||
         ! (lock->lock = PR_NewLock()) )
     {
         if(lock)
         {
             free(lock);
             lock = NULL;
         }
     }
--- a/js/jsd/jsd_lock.h
+++ b/js/jsd/jsd_lock.h
@@ -17,17 +17,17 @@
 
 /*
  * NOTE: These locks must be reentrant in the sense that they support
  * nested calls to lock and unlock.
  */
 
 typedef struct JSDStaticLock JSDStaticLock;
 
-extern void*
+extern JSDStaticLock*
 jsd_CreateLock();
 
 extern void
 jsd_Lock(JSDStaticLock* lock);
 
 extern void
 jsd_Unlock(JSDStaticLock* lock);
 
rename from js/jsd/jsd_obj.c
rename to js/jsd/jsd_obj.cpp
--- a/js/jsd/jsd_obj.c
+++ b/js/jsd/jsd_obj.cpp
@@ -73,38 +73,16 @@ static void
         jsd_DropAtom(jsdc, jsdobj->newURL);
     if(jsdobj->ctorURL)
         jsd_DropAtom(jsdc, jsdobj->ctorURL);
     if(jsdobj->ctorName)
         jsd_DropAtom(jsdc, jsdobj->ctorName);
     free(jsdobj);
 }
 
-static JSDObject*
-_createJSDObject(JSDContext* jsdc, JSContext *cx, JSObject *obj)
-{
-    JSDObject* jsdobj;
-    JSStackFrame* fp;
-    JSStackFrame* iter = NULL;
-    const char* newURL;
-    jsbytecode* pc;
-
-    JS_ASSERT(JSD_OBJECTS_LOCKED(jsdc));
-
-    jsdobj = (JSDObject*) calloc(1, sizeof(JSDObject));
-    if (jsdobj)
-    {
-        JS_INIT_CLIST(&jsdobj->links);
-        JS_APPEND_LINK(&jsdobj->links, &jsdc->objectsList);
-        jsdobj->obj = obj;
-        JS_HashTableAdd(jsdc->objectsTable, obj, jsdobj);
-    }
-    return jsdobj;
-}
-
 void
 jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
                  JSStackFrame *fp)
 {
     JSDObject* jsdobj;
     JSScript* script;
     JSDScript* jsdscript;
     const char* ctorURL;
rename from js/jsd/jsd_scpt.c
rename to js/jsd/jsd_scpt.cpp
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.cpp
@@ -549,17 +549,16 @@ JSBool
 jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
                unsigned startLine, unsigned maxLines,
                unsigned* count, unsigned** retLines, uintptr_t** retPCs)
 {
     JSCompartment* oldCompartment;
     unsigned first = jsdscript->lineBase;
     unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
     JSBool ok;
-    unsigned *lines;
     jsbytecode **pcs;
     unsigned i;
 
     if (last < startLine)
         return JS_TRUE;
 
     oldCompartment = JS_EnterCompartmentOfScript(jsdc->dumbContext, jsdscript->script);
 
rename from js/jsd/jsd_stak.c
rename to js/jsd/jsd_stak.cpp
--- a/js/jsd/jsd_stak.c
+++ b/js/jsd/jsd_stak.cpp
@@ -296,17 +296,16 @@ jsd_GetScopeChainForStackFrame(JSDContex
     return jsdval;
 }
 
 JSDValue*
 jsd_GetThisForStackFrame(JSDContext* jsdc, 
                          JSDThreadState* jsdthreadstate,
                          JSDStackFrameInfo* jsdframe)
 {
-    JSObject* obj;
     JSDValue* jsdval = NULL;
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
         JSBool ok;
         jsval thisval;
         JS_BeginRequest(jsdthreadstate->context);
rename from js/jsd/jsd_step.c
rename to js/jsd/jsd_step.cpp
rename from js/jsd/jsd_text.c
rename to js/jsd/jsd_text.cpp
--- a/js/jsd/jsd_text.c
+++ b/js/jsd/jsd_text.cpp
@@ -115,23 +115,16 @@ static JSDSourceText*
     JSDSourceText* jsdsrc = _newSource(jsdc, url);
     if( ! jsdsrc )
         return NULL;
     JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources);
     return jsdsrc;
 }
 
 static void
-_moveSourceToFront(JSDContext* jsdc, JSDSourceText* jsdsrc)
-{
-    JS_REMOVE_LINK(&jsdsrc->links);
-    JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources);
-}
-
-static void
 _moveSourceToRemovedList(JSDContext* jsdc, JSDSourceText* jsdsrc)
 {
     _clearText(jsdc, jsdsrc);
     JS_REMOVE_LINK(&jsdsrc->links);
     JS_INSERT_LINK(&jsdsrc->links, &jsdc->removedSources);
 }
 
 static void
@@ -431,17 +424,17 @@ jsd_AppendUCSourceText(JSDContext* jsdc,
     int remaining = length;
 
     if(!text || !length)
         return jsd_AppendSourceText(jsdc, jsdsrc, NULL, 0, status);
 
     JSD_LOCK_SOURCE_TEXT(jsdc);
     if(!buf)
     {
-        buf = malloc(UNICODE_TRUNCATE_BUF_SIZE);
+        buf = js_pod_malloc<char>(UNICODE_TRUNCATE_BUF_SIZE);
         if(!buf)
         {
             JSD_UNLOCK_SOURCE_TEXT(jsdc);
             return NULL;
         }
     }
     while(remaining && jsdsrc) {
         int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE;
rename from js/jsd/jsd_val.c
rename to js/jsd/jsd_val.cpp
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.cpp
@@ -170,17 +170,16 @@ jsd_GetValueDouble(JSDContext* jsdc, JSD
 JSString*
 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSExceptionState* exceptionState;
     JSCompartment* oldCompartment = NULL;
     jsval stringval;
     JSString *string;
-    JSBool needWrap;
     JSObject *scopeObj;
 
     if(jsdval->string)
         return jsdval->string;
 
     /* Reuse the string without copying or re-rooting it */
     if(JSVAL_IS_STRING(jsdval->val)) {
         jsdval->string = JSVAL_TO_STRING(jsdval->val);
rename from js/jsd/jsdebug.c
rename to js/jsd/jsdebug.cpp
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.cpp
@@ -105,17 +105,16 @@ JSD_ClearAllProfileData(JSDContext *jsdc
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     jsd_ClearAllProfileData(jsdc);    
 }
 
 JSD_PUBLIC_API(void)
 JSD_SetContextFlags(JSDContext *jsdc, uint32_t flags)
 {
-    uint32_t oldFlags = jsdc->flags;
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     jsdc->flags = flags;
     if (flags & JSD_COLLECT_PROFILE_DATA) {
         /* Need to reenable our call hooks now */
         JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
         JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
     }
 }
@@ -886,54 +885,54 @@ JSD_IsLockingAndThreadIdSupported()
 {
 #ifdef JSD_THREADSAFE
     return JS_TRUE;
 #else
     return JS_FALSE;
 #endif
 }
 
-JSD_PUBLIC_API(void*)
+JSD_PUBLIC_API(JSDStaticLock*)
 JSD_CreateLock()
 {
 #ifdef JSD_THREADSAFE
     return jsd_CreateLock();
 #else
     return (void*)1;
 #endif
 }
 
 JSD_PUBLIC_API(void)
-JSD_Lock(void* lock)
+JSD_Lock(JSDStaticLock* lock)
 {
 #ifdef JSD_THREADSAFE
     jsd_Lock(lock);
 #endif
 }
 
 JSD_PUBLIC_API(void)
-JSD_Unlock(void* lock)
+JSD_Unlock(JSDStaticLock* lock)
 {
 #ifdef JSD_THREADSAFE
     jsd_Unlock(lock);
 #endif
 }
 
 JSD_PUBLIC_API(JSBool)
-JSD_IsLocked(void* lock)
+JSD_IsLocked(JSDStaticLock* lock)
 {
 #if defined(JSD_THREADSAFE) && defined(DEBUG)
     return jsd_IsLocked(lock);
 #else
     return JS_TRUE;
 #endif
 }
 
 JSD_PUBLIC_API(JSBool)
-JSD_IsUnlocked(void* lock)
+JSD_IsUnlocked(JSDStaticLock* lock)
 {
 #if defined(JSD_THREADSAFE) && defined(DEBUG)
     return ! jsd_IsLocked(lock);
 #else
     return JS_TRUE;
 #endif
 }
 
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -1081,54 +1081,56 @@ JSD_SetErrorReporter(JSDContext*       j
 extern JSD_PUBLIC_API(JSBool)
 JSD_GetErrorReporter(JSDContext*        jsdc,
                      JSD_ErrorReporter* reporter,
                      void**             callerdata);
 
 /***************************************************************************/
 /* Generic locks that callers can use for their own purposes */
 
+struct JSDStaticLock;
+
 /*
 * Is Locking and GetThread supported in this build?
 */
 extern JSD_PUBLIC_API(JSBool)
 JSD_IsLockingAndThreadIdSupported();
 
 /*
 * Create a reentrant/nestable lock
 */
-extern JSD_PUBLIC_API(void*)
+extern JSD_PUBLIC_API(JSDStaticLock*)
 JSD_CreateLock();
 
 /*
 * Aquire lock for this thread (or block until available). Increments a
 * counter if this thread already owns the lock.
 */
 extern JSD_PUBLIC_API(void)
-JSD_Lock(void* lock);
+JSD_Lock(JSDStaticLock* lock);
 
 /*
 * Release lock for this thread (or decrement the counter if JSD_Lock
 * was previous called more than once).
 */
 extern JSD_PUBLIC_API(void)
-JSD_Unlock(void* lock);
+JSD_Unlock(JSDStaticLock* lock);
 
 /*
 * For debugging only if not (JS_THREADSAFE AND DEBUG) then returns JS_TRUE
 *    So JSD_IsLocked(lock) may not equal !JSD_IsUnlocked(lock)
 */
 extern JSD_PUBLIC_API(JSBool)
-JSD_IsLocked(void* lock);
+JSD_IsLocked(JSDStaticLock* lock);
 
 /*
 * See above...
 */
 extern JSD_PUBLIC_API(JSBool)
-JSD_IsUnlocked(void* lock);
+JSD_IsUnlocked(JSDStaticLock* lock);
 
 /*
 * return an ID uniquely identifying this thread.
 */
 extern JSD_PUBLIC_API(void*)
 JSD_CurrentThread();
 
 /***************************************************************************/
rename from js/jsd/jsdstubs.c
rename to js/jsd/jsdstubs.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/args-attributes.js
@@ -0,0 +1,75 @@
+function strictArgs() {
+    return (function (a, b, c) {'use strict'; return arguments; })(1, 2);
+}
+
+function normalArgs() {
+    return (function (a, b, c) { return arguments; })(1, 2);
+}
+
+function checkProperty(options, prop, shouldThrow) {
+    var desc, orig;
+    var obj = options.strict ? strictArgs() : normalArgs();
+    var objType = options.strict ? "strict arguments." : "normal arguments.";
+
+    function check() {
+        orig = Object.getOwnPropertyDescriptor(obj, prop);
+
+        var threw = false;
+        try {
+            obj[prop] = obj[prop];
+        }
+        catch (e) {
+            threw = true;
+        }
+        assertEq(threw, shouldThrow, objType + prop + " threw");
+
+        if (orig === undefined) {
+            // The property wasn't defined, so we can skip it.
+            return;
+        }
+
+        desc = Object.getOwnPropertyDescriptor(obj, prop);
+        if ("value" in orig) {
+            assertEq(desc.value, orig.value, objType + prop + " value");
+        } else {
+            assertEq(desc.get, orig.get, objType + prop + " get");
+            assertEq(desc.set, orig.set, objType + prop + " set");
+        }
+        assertEq(desc.writable, orig.writable, objType + prop + " writable");
+        assertEq(desc.enumerable, orig.enumerable, objType + prop + " enumerable");
+        assertEq(desc.configurable, orig.configurable, objType + prop + " configurable");
+    }
+
+    check();
+
+    if (orig && orig.configurable) {
+        if(options.refresh) { obj = options.strict ? strictArgs() : normalArgs(); }
+        Object.defineProperty(obj, prop, {writable: false, enumerable: true});
+        check();
+
+        if(options.refresh) { obj = options.strict ? strictArgs() : normalArgs(); }
+        Object.defineProperty(obj, prop, {writable: true, enumerable: false});
+        check();
+
+        if(options.refresh) { obj = options.strict ? strictArgs() : normalArgs(); }
+        Object.defineProperty(obj, prop, {writable: false, configurable: false});
+        check();
+    }
+}
+
+checkProperty({strict: true, refresh: true}, 'callee', true);
+checkProperty({strict: true, refresh: false}, 'callee', true);
+checkProperty({strict: false, refresh: true}, 'callee', false);
+checkProperty({strict: false, refresh: false}, 'callee', false);
+
+checkProperty({strict: true, refresh: true}, 'length', false);
+checkProperty({strict: true, refresh: false}, 'length', false);
+checkProperty({strict: false, refresh: true}, 'length', false);
+checkProperty({strict: false, refresh: false}, 'length', false);
+
+for (var i = 0; i <= 5; i++) {
+    checkProperty({strict: true, refresh: true}, "" + i, false);
+    checkProperty({strict: true, refresh: false}, "" + i, false);
+    checkProperty({strict: false, refresh: true}, "" + i, false);
+    checkProperty({strict: false, refresh: false}, "" + i, false);
+}
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2032,19 +2032,18 @@ JS_EnumerateStandardClasses(JSContext *c
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     /*
      * Check whether we need to bind 'undefined' and define it if so.
      * Since ES5 15.1.1.3 undefined can't be deleted.
      */
     RootedPropertyName undefinedName(cx, cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
-    RootedId undefinedId(cx, NameToId(undefinedName));
     RootedValue undefinedValue(cx, UndefinedValue());
-    if (!obj->nativeContains(cx, undefinedId) &&
+    if (!obj->nativeContains(cx, undefinedName) &&
         !JSObject::defineProperty(cx, obj, undefinedName, undefinedValue,
                                   JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY)) {
         return false;
     }
 
     /* Initialize any classes that have not been initialized yet. */
     for (unsigned i = 0; standard_class_atoms[i].init; i++) {
@@ -2109,33 +2108,31 @@ AddNameToArray(JSContext *cx, PropertyNa
         JS_ASSERT(i < ida->length);
     }
     ida->vector[i].init(NameToId(name));
     *ip = i + 1;
     return ida;
 }
 
 static JSIdArray *
-EnumerateIfResolved(JSContext *cx, JSHandleObject obj, PropertyName *name, JSIdArray *ida,
-                    int *ip, JSBool *foundp)
-{
-    RootedId id(cx, NameToId(name));
-    *foundp = obj->nativeContains(cx, id);
+EnumerateIfResolved(JSContext *cx, Handle<JSObject*> obj, Handle<PropertyName*> name,
+                    JSIdArray *ida, int *ip, JSBool *foundp)
+{
+    *foundp = obj->nativeContains(cx, name);
     if (*foundp)
         ida = AddNameToArray(cx, name, ida, ip);
     return ida;
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *objArg, JSIdArray *ida)
 {
     RootedObject obj(cx, objArg);
     JSRuntime *rt;
     int i, j, k;
-    PropertyName *name;
     JSBool found;
     JSClassInitializerOp init;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, ida);
     rt = cx->runtime;
     if (ida) {
@@ -2143,17 +2140,17 @@ JS_EnumerateResolvedStandardClasses(JSCo
     } else {
         ida = NewIdArray(cx, 8);
         if (!ida)
             return NULL;
         i = 0;
     }
 
     /* Check whether 'undefined' has been resolved and enumerate it if so. */
-    name = rt->atomState.typeAtoms[JSTYPE_VOID];
+    Rooted<PropertyName*> name(cx, rt->atomState.typeAtoms[JSTYPE_VOID]);
     ida = EnumerateIfResolved(cx, obj, name, ida, &i, &found);
     if (!ida)
         return NULL;
 
     /* Enumerate only classes that *have* been resolved. */
     for (j = 0; standard_class_atoms[j].init; j++) {
         name = OFFSET_TO_NAME(rt, standard_class_atoms[j].atomOffset);
         ida = EnumerateIfResolved(cx, obj, name, ida, &i, &found);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1367,17 +1367,17 @@ typedef bool
 typedef bool
 (* JSJitMethodOp)(JSContext *cx, JSHandleObject thisObj,
                   void *specializedThis, unsigned argc, JS::Value *vp);
 
 struct JSJitInfo {
     JSJitPropertyOp op;
     uint32_t protoID;
     uint32_t depth;
-    bool isInfallible;    /* Is op fallible? Getters only */
+    bool isInfallible;    /* Is op fallible? False in setters. */
     bool isConstant;      /* Getting a construction-time constant? */
 };
 
 static JS_ALWAYS_INLINE const JSJitInfo *
 FUNCTION_VALUE_TO_JITINFO(const JS::Value& v)
 {
     JS_ASSERT(js::GetObjectClass(&v.toObject()) == &js::FunctionClass);
     return reinterpret_cast<js::shadow::Function *>(&v.toObject())->jitinfo;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5105,17 +5105,17 @@ TypeDynamicResult(JSContext *cx, JSScrip
      *
      * This can prevent a significant amount of recompilation in scripts which
      * use these operations extensively, principally autotranslated code.
      */
 
     jsbytecode *ignorePC = pc + GetBytecodeLength(pc);
     if (*ignorePC == JSOP_POP) {
         /* Value is ignored. */
-    } if (*ignorePC == JSOP_INT8 && GET_INT8(ignorePC) == -1) {
+    } else if (*ignorePC == JSOP_INT8 && GET_INT8(ignorePC) == -1) {
         ignorePC += JSOP_INT8_LENGTH;
         if (*ignorePC != JSOP_BITAND)
             ignorePC = NULL;
     } else if (*ignorePC == JSOP_ZERO) {
         ignorePC += JSOP_ZERO_LENGTH;
         if (*ignorePC != JSOP_BITOR)
             ignorePC = NULL;
     } else {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -307,22 +307,16 @@ struct JSObject : public js::ObjectImpl
     inline bool canRemoveLastProperty();
 
     /*
      * Update the slot span directly for a dictionary object, and allocate
      * slots to cover the new span if necessary.
      */
     bool setSlotSpan(JSContext *cx, uint32_t span);
 
-    inline bool nativeContains(JSContext *cx, js::HandleId id);
-    inline bool nativeContains(JSContext *cx, js::HandleShape shape);
-
-    inline bool nativeContainsNoAllocation(jsid id);
-    inline bool nativeContainsNoAllocation(const js::Shape &shape);
-
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
   public:
     inline bool setDelegate(JSContext *cx);
 
     inline bool isBoundFunction() const;
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -931,40 +931,16 @@ JSObject::nativeSetSlot(unsigned slot, c
 inline void
 JSObject::nativeSetSlotWithType(JSContext *cx, js::Shape *shape, const js::Value &value)
 {
     nativeSetSlot(shape->slot(), value);
     js::types::AddTypePropertyId(cx, this, shape->propid(), value);
 }
 
 inline bool
-JSObject::nativeContains(JSContext *cx, js::HandleId id)
-{
-    return nativeLookup(cx, id) != NULL;
-}
-
-inline bool
-JSObject::nativeContains(JSContext *cx, js::HandleShape shape)
-{
-    return nativeLookup(cx, shape->propid()) == shape;
-}
-
-inline bool
-JSObject::nativeContainsNoAllocation(jsid id)
-{
-    return nativeLookupNoAllocation(id) != NULL;
-}
-
-inline bool
-JSObject::nativeContainsNoAllocation(const js::Shape &shape)
-{
-    return nativeLookupNoAllocation(shape.propid()) == &shape;
-}
-
-inline bool
 JSObject::nativeEmpty() const
 {
     return lastProperty()->isEmptyShape();
 }
 
 inline uint32_t
 JSObject::propertyCount() const
 {
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -158,18 +158,18 @@ PropertyCache::fullTest(JSContext *cx, j
         if (!tmp || !tmp->isNative())
             break;
         pobj = tmp;
         protoIndex--;
     }
 
     if (pobj->lastProperty() == entry->pshape) {
 #ifdef DEBUG
-        PropertyName *name = GetNameFromBytecode(cx, script, pc, op);
-        JS_ASSERT(pobj->nativeContainsNoAllocation(NameToId(name)));
+        Rooted<PropertyName*> name(cx, GetNameFromBytecode(cx, script, pc, op));
+        JS_ASSERT(pobj->nativeContainsNoAllocation(name));
 #endif
         *pobjp = pobj;
         return NULL;
     }
 
     PCMETER(vcapmisses++);
     return GetNameFromBytecode(cx, script, pc, op);
 }
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -157,16 +157,22 @@ ArgGetter(JSContext *cx, HandleObject ob
 }
 
 static JSBool
 ArgSetter(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, MutableHandleValue vp)
 {
     if (!obj->isNormalArguments())
         return true;
 
+    unsigned attrs;
+    if (!baseops::GetAttributes(cx, obj, id, &attrs))
+        return false;
+    JS_ASSERT(!(attrs & JSPROP_READONLY));
+    attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
+
     NormalArgumentsObject &argsobj = obj->asNormalArguments();
     JSScript *script = argsobj.containingScript();
 
     if (JSID_IS_INT(id)) {
         unsigned arg = unsigned(JSID_TO_INT(id));
         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
             argsobj.setElement(arg, vp);
             if (arg < script->function()->nargs) {
@@ -186,17 +192,17 @@ ArgSetter(JSContext *cx, HandleObject ob
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value. Note also that we must define the property instead
      * of setting it in case the user has changed the prototype to an object
      * that has a setter for this id.
      */
     RootedValue value(cx);
     return baseops::DeleteGeneric(cx, obj, id, &value, false) &&
-           baseops::DefineGeneric(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
+           baseops::DefineGeneric(cx, obj, id, vp, NULL, NULL, attrs);
 }
 
 static JSBool
 args_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
              MutableHandleObject objp)
 {
     objp.set(NULL);
 
@@ -279,37 +285,43 @@ StrictArgGetter(JSContext *cx, HandleObj
 }
 
 static JSBool
 StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, MutableHandleValue vp)
 {
     if (!obj->isStrictArguments())
         return true;
 
+    unsigned attrs;
+    if (!baseops::GetAttributes(cx, obj, id, &attrs))
+        return false;
+    JS_ASSERT(!(attrs & JSPROP_READONLY));
+    attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
+
     Rooted<StrictArgumentsObject*> argsobj(cx, &obj->asStrictArguments());
 
     if (JSID_IS_INT(id)) {
         unsigned arg = unsigned(JSID_TO_INT(id));
         if (arg < argsobj->initialLength()) {
             argsobj->setElement(arg, vp);
             return true;
         }
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
     }
 
     /*
-     * For simplicity we use delete/set to replace the property with one
+     * For simplicity we use delete/define to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value.
      */
     RootedValue value(cx);
     return baseops::DeleteGeneric(cx, argsobj, id, &value, strict) &&
-           baseops::SetPropertyHelper(cx, argsobj, argsobj, id, 0, vp, strict);
+           baseops::DefineGeneric(cx, argsobj, id, vp, NULL, NULL, attrs);
 }
 
 static JSBool
 strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                    MutableHandleObject objp)
 {
     objp.set(NULL);
 
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -60,16 +60,52 @@ js::ObjectImpl::nativeLookupNoAllocation
 
 inline js::Shape *
 js::ObjectImpl::nativeLookupNoAllocation(PropertyName *name)
 {
     return nativeLookupNoAllocation(PropertyId(name));
 }
 
 inline bool
+js::ObjectImpl::nativeContains(JSContext *cx, JS::Handle<jsid> id)
+{
+    return nativeLookup(cx, id) != NULL;
+}
+
+inline bool
+js::ObjectImpl::nativeContains(JSContext *cx, JS::Handle<PropertyName*> name)
+{
+    return nativeLookup(cx, name) != NULL;
+}
+
+inline bool
+js::ObjectImpl::nativeContains(JSContext *cx, JS::Handle<Shape*> shape)
+{
+    return nativeLookup(cx, shape->propid()) == shape;
+}
+
+inline bool
+js::ObjectImpl::nativeContainsNoAllocation(jsid id)
+{
+    return nativeLookupNoAllocation(id) != NULL;
+}
+
+inline bool
+js::ObjectImpl::nativeContainsNoAllocation(PropertyName *name)
+{
+    return nativeLookupNoAllocation(name) != NULL;
+}
+
+inline bool
+js::ObjectImpl::nativeContainsNoAllocation(Shape &shape)
+{
+    return nativeLookupNoAllocation(shape.propid()) == &shape;
+}
+
+inline bool
 js::ObjectImpl::isExtensible() const
 {
     return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE);
 }
 
 inline bool
 js::ObjectImpl::isDenseArray() const
 {
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -1157,16 +1157,24 @@ class ObjectImpl : public gc::Cell
     Shape * nativeLookup(JSContext *cx, jsid id);
     inline Shape * nativeLookup(JSContext *cx, PropertyId pid);
     inline Shape * nativeLookup(JSContext *cx, PropertyName *name);
 
     Shape * nativeLookupNoAllocation(jsid id);
     inline Shape * nativeLookupNoAllocation(PropertyId pid);
     inline Shape * nativeLookupNoAllocation(PropertyName *name);
 
+    inline bool nativeContains(JSContext *cx, Handle<jsid> id);
+    inline bool nativeContains(JSContext *cx, Handle<PropertyName*> name);
+    inline bool nativeContains(JSContext *cx, Handle<Shape*> shape);
+
+    inline bool nativeContainsNoAllocation(jsid id);
+    inline bool nativeContainsNoAllocation(PropertyName *name);
+    inline bool nativeContainsNoAllocation(Shape &shape);
+
     inline Class *getClass() const;
     inline JSClass *getJSClass() const;
     inline bool hasClass(const Class *c) const;
     inline const ObjectOps *getOps() const;
 
     /*
      * An object is a delegate if it is on another object's prototype or scope
      * chain, and therefore the delegate might be asked implicitly to get or
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -615,16 +615,17 @@ nsBidiPresUtils::Resolve(nsBlockFrame* a
 
   if (ch != 0) {
     bpd.PopBidiControl();
   }
 
   BidiParagraphData* subParagraph = bpd.GetSubParagraph();
   if (subParagraph->BufferLength()) {
     ResolveParagraph(aBlockFrame, subParagraph);
+    subParagraph->EmptyBuffer();
   }
   return ResolveParagraph(aBlockFrame, &bpd);
 }
 
 nsresult
 nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
                                   BidiParagraphData* aBpd)
 {
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -183,16 +183,17 @@ static PRLogModuleInfo * kPrintingLogMod
 #define PRT_YESNO(_p) ((_p)?"YES":"NO")
 #else
 #define PRT_YESNO(_p)
 #define PR_PL(_p1)
 #endif
 //-----------------------------------------------------
 
 class DocumentViewerImpl;
+class nsPrintEventDispatcher;
 
 // a small delegate class used to avoid circular references
 
 class nsDocViewerSelectionListener : public nsISelectionListener
 {
 public:
 
   // nsISupports interface...
@@ -431,16 +432,17 @@ protected:
   unsigned                         mPrintIsPending : 1;
   unsigned                         mPrintDocIsFullyLoaded : 1;
   nsCOMPtr<nsIPrintSettings>       mCachedPrintSettings;
   nsCOMPtr<nsIWebProgressListener> mCachedPrintWebProgressListner;
 
   nsCOMPtr<nsPrintEngine>          mPrintEngine;
   float                            mOriginalPrintPreviewScale;
   float                            mPrintPreviewZoom;
+  nsAutoPtr<nsPrintEventDispatcher> mBeforeAndAfterPrint;
 #endif // NS_PRINT_PREVIEW
 
 #ifdef DEBUG
   FILE* mDebugFile;
 #endif // DEBUG
 #endif // NS_PRINTING
 
   /* character set member data */
@@ -1500,16 +1502,17 @@ DocumentViewerImpl::Destroy()
   //
   // So we flip the bool to remember that the document is going away
   // and we can clean up and abort later after returning from the Print Dialog
   if (mPrintEngine) {
     if (mPrintEngine->CheckBeforeDestroy()) {
       return NS_OK;
     }
   }
+  mBeforeAndAfterPrint = nullptr;
 #endif
 
   // Don't let the document get unloaded while we are printing.
   // this could happen if we hit the back button during printing.
   // We also keep the viewer from being cached in session history, since
   // we require all documents there to be sanitized.
   if (mDestroyRefCount != 0) {
     --mDestroyRefCount;
@@ -3606,17 +3609,18 @@ DocumentViewerImpl::Print(nsIPrintSettin
   // the only time we can print more than one job at a time is the regression tests
   if (GetIsPrinting()) {
     // Let the user know we are not ready to print.
     rv = NS_ERROR_NOT_AVAILABLE;
     nsPrintEngine::ShowPrintErrorDialog(rv);
     return rv;
   }
 
-  nsPrintEventDispatcher beforeAndAfterPrint(mDocument);
+  nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
+    new nsPrintEventDispatcher(mDocument));
   NS_ENSURE_STATE(!GetIsPrinting());
   // If we are hosting a full-page plugin, tell it to print
   // first. It shows its own native print UI.
   nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(mDocument));
   if (pDoc)
     return pDoc->Print();
 
   if (!mPrintEngine) {
@@ -3634,17 +3638,19 @@ DocumentViewerImpl::Print(nsIPrintSettin
 #endif
                                   );
     if (NS_FAILED(rv)) {
       mPrintEngine->Destroy();
       mPrintEngine = nullptr;
       return rv;
     }
   }
-
+  if (mPrintEngine->HasPrintCallbackCanvas()) {
+    mBeforeAndAfterPrint = beforeAndAfterPrint;
+  }
   rv = mPrintEngine->Print(aPrintSettings, aWebProgressListener);
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 }
 
 NS_IMETHODIMP
@@ -3680,17 +3686,18 @@ DocumentViewerImpl::PrintPreview(nsIPrin
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDOMDocument> domDoc;
   aChildDOMWin->GetDocument(getter_AddRefs(domDoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   NS_ENSURE_STATE(doc);
 
-  nsPrintEventDispatcher beforeAndAfterPrint(doc);
+  nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
+    new nsPrintEventDispatcher(doc));
   NS_ENSURE_STATE(!GetIsPrinting());
   if (!mPrintEngine) {
     mPrintEngine = new nsPrintEngine();
 
     rv = mPrintEngine->Initialize(this, mContainer, doc,
                                   float(mDeviceContext->AppUnitsPerCSSInch()) /
                                   float(mDeviceContext->AppUnitsPerDevPixel()) /
                                   mPageZoom,
@@ -3701,17 +3708,19 @@ DocumentViewerImpl::PrintPreview(nsIPrin
 #endif
                                   );
     if (NS_FAILED(rv)) {
       mPrintEngine->Destroy();
       mPrintEngine = nullptr;
       return rv;
     }
   }
-
+  if (mPrintEngine->HasPrintCallbackCanvas()) {
+    mBeforeAndAfterPrint = beforeAndAfterPrint;
+  }
   rv = mPrintEngine->PrintPreview(aPrintSettings, aChildDOMWin, aWebProgressListener);
   mPrintPreviewZoomed = false;
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 #else
   return NS_ERROR_FAILURE;
@@ -4106,16 +4115,20 @@ DocumentViewerImpl::SetIsPrinting(bool a
   // Set all the docShells in the docshell tree to be printing.
   // that way if anyone of them tries to "navigate" it can't
   nsCOMPtr<nsIDocShellTreeNode> docShellTreeNode(do_QueryReferent(mContainer));
   if (docShellTreeNode || !aIsPrinting) {
     SetIsPrintingInDocShellTree(docShellTreeNode, aIsPrinting, true);
   } else {
     NS_WARNING("Did you close a window before printing?");
   }
+
+  if (!aIsPrinting) {
+    mBeforeAndAfterPrint = nullptr;
+  }
 #endif
 }
 
 //------------------------------------------------------------
 // The PrintEngine holds the current value
 // this called from inside the DocViewer.
 // XXX it always returns false for subdocuments
 bool
@@ -4136,16 +4149,19 @@ DocumentViewerImpl::SetIsPrintPreview(bo
 {
 #ifdef NS_PRINTING
   // Set all the docShells in the docshell tree to be printing.
   // that way if anyone of them tries to "navigate" it can't
   nsCOMPtr<nsIDocShellTreeNode> docShellTreeNode(do_QueryReferent(mContainer));
   if (docShellTreeNode || !aIsPrintPreview) {
     SetIsPrintingInDocShellTree(docShellTreeNode, aIsPrintPreview, true);
   }
+  if (!aIsPrintPreview) {
+    mBeforeAndAfterPrint = nullptr;
+  }
 #endif
   if (!aIsPrintPreview) {
     if (mPresShell) {
       DestroyPresShell();
     }
     mWindow = nullptr;
     mViewManager = nullptr;
     mPresContext = nullptr;
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -255,26 +255,28 @@ already_AddRefed<Layer>
 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               nsDisplayItem* aItem)
 {
   nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
   nsHTMLCanvasElement* element = static_cast<nsHTMLCanvasElement*>(GetContent());
   nsIntSize canvasSize = GetCanvasSize();
 
+  nsPresContext* presContext = PresContext();
+  element->HandlePrintCallback(presContext->Type());
+
   if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
     return nullptr;
 
   CanvasLayer* oldLayer = static_cast<CanvasLayer*>
     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
   nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
   if (!layer)
     return nullptr;
 
-  nsPresContext* presContext = PresContext();
   gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
                       presContext->AppUnitsToGfxUnits(area.y),
                       presContext->AppUnitsToGfxUnits(area.width),
                       presContext->AppUnitsToGfxUnits(area.height));
 
   // Transform the canvas into the right place
   gfxMatrix transform;
   transform.Translate(r.TopLeft());
--- a/layout/generic/nsIPageSequenceFrame.h
+++ b/layout/generic/nsIPageSequenceFrame.h
@@ -5,16 +5,17 @@
 #ifndef nsIPageSequenceFrame_h___
 #define nsIPageSequenceFrame_h___
 
 #include "nsQueryFrame.h"
 #include "nsRect.h"
 
 class nsPresContext;
 class nsIPrintSettings;
+class nsITimerCallback;
 
 /**
  * Interface for accessing special capabilities of the page sequence frame.
  *
  * Today all that exists are member functions for printing.
  */
 class nsIPageSequenceFrame : public nsQueryFrame
 {
@@ -33,17 +34,20 @@ public:
    *          NS_ERROR_INVALID_ARG if printing a range of pages (not all pages)
    *            and the start page is greater than the total number of pages
    *          NS_ERROR_FAILURE if there is an error
    */
   NS_IMETHOD StartPrint(nsPresContext*  aPresContext,
                         nsIPrintSettings* aPrintOptions,
                         PRUnichar* aDocTitle,
                         PRUnichar* aDocURL) = 0;
+
+  NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) = 0;
   NS_IMETHOD PrintNextPage() = 0;
+  NS_IMETHOD ResetPrintCanvasList() = 0;
   NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum) = 0;
   NS_IMETHOD GetNumPages(int32_t* aNumPages) = 0;
   NS_IMETHOD IsDoingPrintRange(bool* aDoing) = 0;
   NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage) = 0;
 
   NS_IMETHOD DoPageEnd() = 0;
   NS_IMETHOD SetSelectionHeight(nscoord aYOffset, nscoord aHeight) = 0;
 
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -7,22 +7,26 @@
 #include "nsSimplePageSequence.h"
 #include "nsPresContext.h"
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsIPrintSettings.h"
 #include "nsPageFrame.h"
+#include "nsSubDocumentFrame.h"
 #include "nsStyleConsts.h"
 #include "nsRegion.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "mozilla/Preferences.h"
+#include "nsHTMLCanvasFrame.h"
+#include "nsHTMLCanvasElement.h"
+#include "nsICanvasRenderingContextInternal.h"
 
 // DateTime Includes
 #include "nsDateTimeFormatCID.h"
 
 #define OFFSET_NOT_SET -1
 
 // Print Options
 #include "nsIPrintOptions.h"
@@ -77,18 +81,20 @@ NS_NewSimplePageSequenceFrame(nsIPresShe
   return new (aPresShell) nsSimplePageSequenceFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)
 
 nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext) :
   nsContainerFrame(aContext),
   mTotalPages(-1),
+  mCurrentCanvasListSetup(false),
   mSelectionHeight(-1),
-  mYSelOffset(0)
+  mYSelOffset(0),
+  mCalledBeginPage(false)
 {
   nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
   mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
 
   // XXX Unsafe to assume successful allocation
   mPageData = new nsSharedPageData();
   mPageData->mHeadFootFont =
     new nsFont(*PresContext()->GetDefaultFont(kGenericFont_serif,
@@ -482,55 +488,77 @@ nsSimplePageSequenceFrame::StartPrint(ns
 
   if (mTotalPages == -1) {
     mTotalPages = totalPages;
   }
 
   return rv;
 }
 
-NS_IMETHODIMP
-nsSimplePageSequenceFrame::PrintNextPage()
+void
+GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray<nsRefPtr<nsHTMLCanvasElement> >* aArr)
 {
-  // Print each specified page
-  // pageNum keeps track of the current page and what pages are printing
-  //
-  // printedPageNum keeps track of the current page number to be printed
-  // Note: When print al the pages or a page range the printed page shows the
-  // actual page number, when printing selection it prints the page number starting
-  // with the first page of the selection. For example if the user has a 
-  // selection that starts on page 2 and ends on page 3, the page numbers when
-  // print are 1 and then two (which is different than printing a page range, where
-  // the page numbers would have been 2 and then 3)
+  for (nsIFrame::ChildListIterator childLists(aFrame);
+    !childLists.IsDone(); childLists.Next()) {
+
+    nsFrameList children = childLists.CurrentList();
+    for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+      nsIFrame* child = e.get();
+
+      // Check if child is a nsHTMLCanvasFrame.
+      nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child);
 
-  if (mCurrentPageFrame == nullptr) {
-    return NS_ERROR_FAILURE;
+      // If there is a canvasFrame, try to get actual canvas element.
+      if (canvasFrame) {
+        nsHTMLCanvasElement* canvas =
+          nsHTMLCanvasElement::FromContent(canvasFrame->GetContent());
+        nsCOMPtr<nsIPrintCallback> printCallback;
+        if (canvas &&
+            NS_SUCCEEDED(canvas->GetMozPrintCallback(getter_AddRefs(printCallback))) &&
+            printCallback) {
+          aArr->AppendElement(canvas);
+          continue;
+        }
+      }
+
+      if (!child->GetFirstPrincipalChild()) {
+        nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child);
+        if (subdocumentFrame) {
+          // Descend into the subdocument
+          nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
+          child = root;
+        }
+      }
+      // The current child is not a nsHTMLCanvasFrame OR it is but there is
+      // no nsHTMLCanvasElement on it. Check if children of `child` might
+      // contain a nsHTMLCanvasElement.
+      GetPrintCanvasElementsInFrame(child, aArr);
+    }
   }
+}
 
+void
+nsSimplePageSequenceFrame::DetermineWhetherToPrintPage()
+{
+  // See whether we should print this page
+  mPrintThisPage = true;
   bool printEvenPages, printOddPages;
   mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages);
   mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages);
 
-  // Begin printing of the document
-  nsDeviceContext *dc = PresContext()->DeviceContext();
-
-  nsresult rv = NS_OK;
-
-  // See whether we should print this page
-  mPrintThisPage = true;
-
   // If printing a range of pages check whether the page number is in the
   // range of pages to print
   if (mDoingPageRange) {
     if (mPageNum < mFromPageNum) {
       mPrintThisPage = false;
     } else if (mPageNum > mToPageNum) {
       mPageNum++;
       mCurrentPageFrame = nullptr;
-      return NS_OK;
+      mPrintThisPage = false;
+      return;
     } else {
       int32_t length = mPageRanges.Length();
     
       // Page ranges are pairs (start, end)
       if (length && (length % 2 == 0)) {
         mPrintThisPage = false;
       
         int32_t i;
@@ -553,18 +581,153 @@ nsSimplePageSequenceFrame::PrintNextPage
     if (!printEvenPages) {
       mPrintThisPage = false;  // don't print even numbered page
     }
   }
   
   if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
     mPrintThisPage = true;
   }
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone)
+{
+  if (!mCurrentPageFrame) {
+    *aDone = true;
+    return NS_ERROR_FAILURE;
+  }
+  
+  DetermineWhetherToPrintPage();
+  // Nothing to do if the current page doesn't get printed OR rendering to
+  // preview. For preview, the `CallPrintCallback` is called from within the
+  // nsHTMLCanvasElement::HandlePrintCallback.
+  if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) {
+    *aDone = true;
+    return NS_OK;
+  }
+
+  // If the canvasList is null, then generate it and start the render
+  // process for all the canvas.
+  if (!mCurrentCanvasListSetup) {
+    mCurrentCanvasListSetup = true;
+    GetPrintCanvasElementsInFrame(mCurrentPageFrame, &mCurrentCanvasList);
+
+    if (mCurrentCanvasList.Length() != 0) {
+      nsresult rv = NS_OK;
+
+      // Begin printing of the document
+      nsDeviceContext *dc = PresContext()->DeviceContext();
+      PR_PL(("\n"));
+      PR_PL(("***************** BeginPage *****************\n"));
+      rv = dc->BeginPage();
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mCalledBeginPage = true;
+      
+      nsRefPtr<nsRenderingContext> renderingContext;
+      dc->CreateRenderingContext(*getter_AddRefs(renderingContext));
+      NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
+
+      nsRefPtr<gfxASurface> renderingSurface =
+          renderingContext->ThebesContext()->CurrentSurface();
+      NS_ENSURE_TRUE(renderingSurface, NS_ERROR_OUT_OF_MEMORY);
+
+      for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+        nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+        nsIntSize size = canvas->GetSize();
+
+        nsRefPtr<gfxASurface> printSurface = renderingSurface->
+           CreateSimilarSurface(
+             gfxASurface::CONTENT_COLOR_ALPHA,
+             size
+           );
+
+        nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0);
+
+        if (!ctx) {
+          continue;
+        }
+
+          // Initialize the context with the new printSurface.
+        ctx->InitializeWithSurface(NULL, printSurface, size.width, size.height);
+
+        // Start the rendering process.
+        nsWeakFrame weakFrame = this;
+        canvas->DispatchPrintCallback(aCallback);
+        NS_ENSURE_STATE(weakFrame.IsAlive());
+      }
+    }
+  }
+  PRInt32 doneCounter = 0;
+  for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+    nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+
+    if (canvas->IsPrintCallbackDone()) {
+      doneCounter++;
+    }
+  }
+  // If all canvas have finished rendering, return true, otherwise false.
+  *aDone = doneCounter == mCurrentCanvasList.Length();
+
+  return NS_OK;
+}
+
+void
+nsSimplePageSequenceFrame::InvalidateInternal(const nsRect& aDamageRect,
+                                              nscoord aX, nscoord aY,
+                                              nsIFrame* aForChild,
+                                              PRUint32 aFlags)
+{
+  // xxx Invalidate the entire frame as otherwise invalidate of printCanvas
+  // don't work properly. This is hopefully no longer necessary once 539356
+  // lands.
+  nsContainerFrame::InvalidateInternal(
+      nsRect(nsPoint(0,0), GetSize()), 0, 0, aForChild, aFlags); 
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::ResetPrintCanvasList()
+{
+  for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+    nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+    canvas->ResetPrintCallback();
+  }
+
+  mCurrentCanvasList.Clear();
+  mCurrentCanvasListSetup = false; 
+  return NS_OK;
+} 
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrintNextPage()
+{
+  // Print each specified page
+  // pageNum keeps track of the current page and what pages are printing
+  //
+  // printedPageNum keeps track of the current page number to be printed
+  // Note: When print al the pages or a page range the printed page shows the
+  // actual page number, when printing selection it prints the page number starting
+  // with the first page of the selection. For example if the user has a 
+  // selection that starts on page 2 and ends on page 3, the page numbers when
+  // print are 1 and then two (which is different than printing a page range, where
+  // the page numbers would have been 2 and then 3)
+
+  if (!mCurrentPageFrame) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = NS_OK;
+
+  DetermineWhetherToPrintPage();
 
   if (mPrintThisPage) {
+    // Begin printing of the document
+    nsDeviceContext* dc = PresContext()->DeviceContext();
+
     // XXX This is temporary fix for printing more than one page of a selection
     // This does a poor man's "dump" pagination (see Bug 89353)
     // It has laid out as one long page and now we are just moving or view up/down 
     // one page at a time and printing the contents of what is exposed by the rect.
     // currently this does not work for IFrames
     // I will soon improve this to work with IFrames 
     bool    continuePrinting = true;
     nscoord width, height;
@@ -582,20 +745,24 @@ nsSimplePageSequenceFrame::PrintNextPage
     // cast the frame to be a page frame
     nsPageFrame * pf = static_cast<nsPageFrame*>(mCurrentPageFrame);
     pf->SetPageNumInfo(mPageNum, mTotalPages);
     pf->SetSharedPageData(mPageData);
 
     int32_t printedPageNum = 1;
     while (continuePrinting) {
       if (PresContext()->IsRootPaginatedDocument()) {
-        PR_PL(("\n"));
-        PR_PL(("***************** BeginPage *****************\n"));
-        rv = dc->BeginPage();
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (!mCalledBeginPage) {
+          PR_PL(("\n"));
+          PR_PL(("***************** BeginPage *****************\n"));
+          rv = dc->BeginPage();
+          NS_ENSURE_SUCCESS(rv, rv);
+        } else {
+          mCalledBeginPage = false;
+        }
       }
 
       PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
 
       nsRefPtr<nsRenderingContext> renderingContext;
       dc->CreateRenderingContext(*getter_AddRefs(renderingContext));
       NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
 
@@ -629,16 +796,18 @@ nsSimplePageSequenceFrame::DoPageEnd()
 {
   nsresult rv = NS_OK;
   if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) {
     PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
     rv = PresContext()->DeviceContext()->EndPage();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  ResetPrintCanvasList();
+
   mPageNum++;
 
   if (mCurrentPageFrame) {
     mCurrentPageFrame = mCurrentPageFrame->GetNextSibling();
   }
   
   return rv;
 }
--- a/layout/generic/nsSimplePageSequence.h
+++ b/layout/generic/nsSimplePageSequence.h
@@ -5,16 +5,17 @@
 #ifndef nsSimplePageSequence_h___
 #define nsSimplePageSequence_h___
 
 #include "nsIPageSequenceFrame.h"
 #include "nsContainerFrame.h"
 #include "nsIPrintSettings.h"
 #include "nsIPrintOptions.h"
 #include "nsIDateTimeFormat.h"
+#include "nsHTMLCanvasElement.h"
 
 //-----------------------------------------------
 // This class maintains all the data that 
 // is used by all the page frame
 // It lives while the nsSimplePageSequenceFrame lives
 class nsSharedPageData {
 public:
   nsSharedPageData();
@@ -71,34 +72,40 @@ public:
   // For Shrink To Fit
   NS_IMETHOD GetSTFPercent(float& aSTFPercent);
 
   // Async Printing
   NS_IMETHOD StartPrint(nsPresContext*  aPresContext,
                         nsIPrintSettings* aPrintSettings,
                         PRUnichar*        aDocTitle,
                         PRUnichar*        aDocURL);
+  NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone);
   NS_IMETHOD PrintNextPage();
+  NS_IMETHOD ResetPrintCanvasList();
   NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum);
   NS_IMETHOD GetNumPages(int32_t* aNumPages);
   NS_IMETHOD IsDoingPrintRange(bool* aDoing);
   NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage);
   NS_IMETHOD DoPageEnd();
 
   // We must allow Print Preview UI to have a background, no matter what the
   // user's settings
   virtual bool HonorPrintBackgroundSettings() { return false; }
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::sequenceFrame
    */
   virtual nsIAtom* GetType() const;
-  
+
+  virtual void InvalidateInternal(const nsRect& aDamageRect,
+                                  nscoord aX, nscoord aY,
+                                  nsIFrame* aForChild,
+                                  PRUint32 aFlags);
 #ifdef DEBUG
   NS_IMETHOD  GetFrameName(nsAString& aResult) const;
 #endif
 
   void PaintPageSequence(nsRenderingContext& aRenderingContext,
                          const nsRect&        aDirtyRect,
                          nsPoint              aPt);
 
@@ -113,38 +120,45 @@ protected:
   void SetPageNumberFormat(PRUnichar * aFormatStr, bool aForPageNumOnly);
 
   // Sets the frame desired size to the size of the viewport, or the given
   // nscoords, whichever is larger. Print scaling is applied in this function.
   void SetDesiredSize(nsHTMLReflowMetrics& aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nscoord aWidth, nscoord aHeight);
 
+  void         DetermineWhetherToPrintPage();
+
   nsMargin mMargin;
 
   // I18N date formatter service which we'll want to cache locally.
   nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
 
   nsSize       mSize;
   nsSharedPageData* mPageData; // data shared by all the nsPageFrames
 
   // Asynch Printing
   nsIFrame *   mCurrentPageFrame;
   int32_t      mPageNum;
   int32_t      mTotalPages;
   int32_t      mPrintRangeType;
   int32_t      mFromPageNum;
   int32_t      mToPageNum;
   nsTArray<int32_t> mPageRanges;
+  nsTArray<nsRefPtr<nsHTMLCanvasElement> > mCurrentCanvasList;
 
   // Selection Printing Info
   nscoord      mSelectionHeight;
   nscoord      mYSelOffset;
 
   // Asynch Printing
   bool mPrintThisPage;
   bool mDoingPageRange;
 
   bool mIsPrintingSelection;
+
+  bool mCalledBeginPage;
+
+  bool mCurrentCanvasListSetup;
 };
 
 #endif /* nsSimplePageSequence_h___ */
 
--- a/layout/printing/nsPagePrintTimer.cpp
+++ b/layout/printing/nsPagePrintTimer.cpp
@@ -47,49 +47,62 @@ nsPagePrintTimer::StartTimer(bool aUseDe
         delay = mDelay;
       }
     }
     mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
   }
   return result;
 }
 
+//nsRunnable
+NS_IMETHODIMP
+nsPagePrintTimer::Run() 
+{
+  bool initNewTimer = true;
+  // Check to see if we are done
+  // inRange will be true if a page is actually printed
+  bool inRange;
+  bool donePrinting;
 
+  // donePrinting will be true if it completed successfully or
+  // if the printing was cancelled
+  donePrinting = mPrintEngine->PrintPage(mPrintObj, inRange);
+  if (donePrinting) {
+    // now clean up print or print the next webshell
+    if (mPrintEngine->DonePrintingPages(mPrintObj, NS_OK)) {
+      initNewTimer = false;
+    }
+  }
+
+  // Note that the Stop() destroys this after the print job finishes
+  // (The PrintEngine stops holding a reference when DonePrintingPages
+  // returns true.)
+  Stop(); 
+  if (initNewTimer) {
+    ++mFiringCount;
+    nsresult result = StartTimer(inRange);
+    if (NS_FAILED(result)) {
+      donePrinting = true;     // had a failure.. we are finished..
+      mPrintEngine->SetIsPrinting(false);
+    }
+  }
+  return NS_OK;
+};
 
 // nsITimerCallback
 NS_IMETHODIMP
 nsPagePrintTimer::Notify(nsITimer *timer)
 {
   if (mDocViewerPrint) {
-    bool initNewTimer = true;
-    // Check to see if we are done
-    // inRange will be true if a page is actually printed
-    bool inRange;
-    // donePrinting will be true if it completed successfully or
-    // if the printing was cancelled
-    bool donePrinting = mPrintEngine->PrintPage(mPrintObj, inRange);
-    if (donePrinting) {
-      // now clean up print or print the next webshell
-      if (mPrintEngine->DonePrintingPages(mPrintObj, NS_OK)) {
-        initNewTimer = false;
-      }
+    bool donePrePrint = mPrintEngine->PrePrintPage();
+
+    if (donePrePrint) {
+      NS_DispatchToMainThread(this);
     }
 
-    // Note that the Stop() destroys this after the print job finishes
-    // (The PrintEngine stops holding a reference when DonePrintingPages
-    // returns true.)
-    Stop(); 
-    if (initNewTimer) {
-      ++mFiringCount;
-      nsresult result = StartTimer(inRange);
-      if (NS_FAILED(result)) {
-        donePrinting = true;     // had a failure.. we are finished..
-        mPrintEngine->SetIsPrinting(false);
-      }
-    }
   }
   return NS_OK;
 }
 
 void 
 nsPagePrintTimer::Init(nsPrintEngine*          aPrintEngine,
                        nsIDocumentViewerPrint* aDocViewerPrint,
                        uint32_t                aDelay)
--- a/layout/printing/nsPagePrintTimer.h
+++ b/layout/printing/nsPagePrintTimer.h
@@ -6,39 +6,43 @@
 #define nsPagePrintTimer_h___
 
 // Timer Includes
 #include "nsITimer.h"
 
 #include "nsIDocumentViewerPrint.h"
 #include "nsPrintObject.h"
 #include "mozilla/Attributes.h"
+#include "nsThreadUtils.h"
 
 class nsPrintEngine;
 
 //---------------------------------------------------
 //-- Page Timer Class
 //---------------------------------------------------
-class nsPagePrintTimer MOZ_FINAL : public nsITimerCallback
+class nsPagePrintTimer MOZ_FINAL : public nsITimerCallback,
+                                   public nsRunnable
 {
 public:
 
   NS_DECL_ISUPPORTS
 
   nsPagePrintTimer();
   ~nsPagePrintTimer();
 
   NS_DECL_NSITIMERCALLBACK
 
   void Init(nsPrintEngine*          aPrintEngine,
             nsIDocumentViewerPrint* aDocViewerPrint,
             uint32_t                aDelay);
 
   nsresult Start(nsPrintObject* aPO);
 
+  NS_IMETHOD Run();
+
   void Stop();
 
 private:
   nsresult StartTimer(bool aUseDelay);
 
   nsPrintEngine*             mPrintEngine;
   nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
   nsCOMPtr<nsITimer>         mTimer;
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -2388,18 +2388,104 @@ nsPrintEngine::ElipseLongString(PRUnicha
       newStr.SetLength(aLen-3);
       newStr.AppendLiteral("...");
       nsMemory::Free(aStr);
       aStr = ToNewUnicode(newStr);
     }
   }
 }
 
+static bool
+DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
+{
+  if (!aDoc) {
+    return true;
+  }
+  Element* root = aDoc->GetRootElement();
+  nsRefPtr<nsContentList> canvases = NS_GetContentList(root,
+                                                       kNameSpaceID_XHTML,
+                                                       NS_LITERAL_STRING("canvas"));
+  PRUint32 canvasCount = canvases->Length(true);
+  for (PRUint32 i = 0; i < canvasCount; ++i) {
+    nsCOMPtr<nsIDOMHTMLCanvasElement> canvas = do_QueryInterface(canvases->Item(i, false));
+    nsCOMPtr<nsIPrintCallback> printCallback;
+    if (canvas && NS_SUCCEEDED(canvas->GetMozPrintCallback(getter_AddRefs(printCallback))) &&
+        printCallback) {
+      // This subdocument has a print callback. Set result and return false to
+      // stop iteration.
+      *static_cast<bool*>(aData) = true;
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool
+DocHasPrintCallbackCanvas(nsIDocument* aDoc)
+{
+  bool result = false;
+  aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
+  return result;
+}
+
+/**
+ * Checks to see if the document this print engine is associated with has any
+ * canvases that have a mozPrintCallback.
+ */
+bool
+nsPrintEngine::HasPrintCallbackCanvas()
+{
+  if (!mDocument) {
+    return false;
+  }
+  // First check this mDocument.
+  bool result = false;
+  DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result));
+  // Also check the sub documents.
+  return result || DocHasPrintCallbackCanvas(mDocument);
+}
+
 //-------------------------------------------------------
 bool
+nsPrintEngine::PrePrintPage()
+{
+  NS_ASSERTION(mPageSeqFrame,  "mPageSeqFrame is null!");
+  NS_ASSERTION(mPrt,           "mPrt is null!");
+
+  // Although these should NEVER be NULL
+  // This is added insurance, to make sure we don't crash in optimized builds
+  if (!mPrt || !mPageSeqFrame) {
+    return true; // means we are done preparing the page.
+  }
+
+  // Check setting to see if someone request it be cancelled
+  bool isCancelled = false;
+  mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
+  if (isCancelled)
+    return true;
+
+  // Ask mPageSeqFrame if the page is ready to be printed.
+  // If the page doesn't get printed at all, the |done| will be |true|.
+  bool done = false;
+  nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
+  if (NS_FAILED(rv)) {
+    // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT,
+    // but I don't really understand why this should be the right thing to do?
+    // Shouldn't |mPrt->mIsAborted| set to true all the time if something
+    // wents wrong?
+    if (rv != NS_ERROR_ABORT) {
+      ShowPrintErrorDialog(rv);
+      mPrt->mIsAborted = true;
+    }
+    done = true;
+  }
+  return done;
+}
+
+bool
 nsPrintEngine::PrintPage(nsPrintObject*    aPO,
                          bool&           aInRange)
 {
   NS_ASSERTION(aPO,            "aPO is null!");
   NS_ASSERTION(mPageSeqFrame,  "mPageSeqFrame is null!");
   NS_ASSERTION(mPrt,           "mPrt is null!");
 
   // Although these should NEVER be NULL
@@ -2410,17 +2496,17 @@ nsPrintEngine::PrintPage(nsPrintObject* 
   }
 
   PR_PL(("-----------------------------------\n"));
   PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
 
   // Check setting to see if someone request it be cancelled
   bool isCancelled = false;
   mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
-  if (isCancelled)
+  if (isCancelled || mPrt->mIsAborted)
     return true;
 
   int32_t pageNum, numPages, endPage;
   mPageSeqFrame->GetCurrentPageNum(&pageNum);
   mPageSeqFrame->GetNumPages(&numPages);
 
   bool donePrinting;
   bool isDoingPrintRange;
@@ -2669,24 +2755,18 @@ nsPrintEngine::GetPageRangeForSelection(
 //-----------------------------------------------------------------
 
 //---------------------------------------------------------------------
 void nsPrintEngine::SetIsPrinting(bool aIsPrinting)
 { 
   mIsDoingPrinting = aIsPrinting;
   // Calling SetIsPrinting while in print preview confuses the document viewer
   // This is safe because we prevent exiting print preview while printing
-  if (!mIsDoingPrintPreview &&
-      mPrt && mPrt->mPrintObject && mPrt->mPrintObject->mDocShell) {
-    nsCOMPtr<nsIContentViewer> viewer;
-    mPrt->mPrintObject->mDocShell->GetContentViewer(getter_AddRefs(viewer));
-    nsCOMPtr<nsIDocumentViewerPrint> docViewerPrint = do_QueryInterface(viewer);
-    if (docViewerPrint) {
-      docViewerPrint->SetIsPrinting(aIsPrinting);
-    }
+  if (!mIsDoingPrintPreview && mDocViewerPrint) {
+    mDocViewerPrint->SetIsPrinting(aIsPrinting);
   }
   if (mPrt && aIsPrinting) {
     mPrt->mPreparingForPrint = true;
   }
 }
 
 //---------------------------------------------------------------------
 void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview) 
@@ -2795,17 +2875,24 @@ nsPrintEngine::IsWindowsInOurSubTree(nsP
 
 //-------------------------------------------------------
 bool
 nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
 {
   //NS_ASSERTION(aPO, "Pointer is null!");
   PR_PL(("****** In DV::DonePrintingPages PO: %p (%s)\n", aPO, aPO?gFrameTypesStr[aPO->mFrameType]:""));
 
-  if (aPO != nullptr) {
+  // If there is a pageSeqFrame, make sure there are no more printCanvas active
+  // that might call |Notify| on the pagePrintTimer after things are cleaned up
+  // and printing was marked as being done.
+  if (mPageSeqFrame) {
+    mPageSeqFrame->ResetPrintCanvasList();
+  }
+
+  if (aPO && !mPrt->mIsAborted) {
     aPO->mHasBeenPrinted = true;
     nsresult rv;
     bool didPrint = PrintDocContent(mPrt->mPrintObject, rv);
     if (NS_SUCCEEDED(rv) && didPrint) {
       PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
       return false;
     }
   }
--- a/layout/printing/nsPrintEngine.h
+++ b/layout/printing/nsPrintEngine.h
@@ -8,16 +8,17 @@
 #include "mozilla/Attributes.h"
 
 #include "nsCOMPtr.h"
 
 #include "nsPrintObject.h"
 #include "nsPrintData.h"
 #include "nsFrameList.h"
 #include "mozilla/Attributes.h"
+#include "nsHTMLCanvasElement.h"
 
 // Interfaces
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserver.h"
 
 // Classes
 class nsPagePrintTimer;
@@ -98,16 +99,18 @@ public:
 
   void SetPrintPO(nsPrintObject* aPO, bool aPrint);
 
   void TurnScriptingOn(bool aDoTurnOn);
   bool CheckDocumentForPPCaching();
   void InstallPrintPreviewListener();
 
   // nsIDocumentViewerPrint Printing Methods
+  bool     HasPrintCallbackCanvas();
+  bool     PrePrintPage();
   bool     PrintPage(nsPrintObject* aPOect, bool& aInRange);
   bool     DonePrintingPages(nsPrintObject* aPO, nsresult aResult);
 
   //---------------------------------------------------------------------
   void BuildDocTree(nsIDocShellTreeNode *      aParentNode,
                     nsTArray<nsPrintObject*> * aDocList,
                     nsPrintObject *            aPO);
   nsresult ReflowDocList(nsPrintObject * aPO, bool aSetPixelScale);
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -47,18 +47,18 @@ fails-if(Android) != text-font-lang.html
 
 == strokeText-path.html strokeText-path-ref.html
 
 # azure quartz uses CGDrawLinearGradient instead of DrawShading
 # so we have less control over degenerate behaviour as tested by this
 # test
 fails-if(azureQuartz) == linear-gradient-1a.html linear-gradient-1-ref.html
 
-# this passes with cairo on 10.7 but not with azure for reasons unknown
-fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)||(azureQuartz&&/Mac\x20OS\x20X\x2010\.7/.test(http.oscpu))) == linear-gradient-1b.html linear-gradient-1-ref.html
+# this passes with cairo on 10.7 and 10.8 but not with azure for reasons unknown
+fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)||(azureQuartz&&/Mac\x20OS\x20X\x2010\.[78]/.test(http.oscpu))) == linear-gradient-1b.html linear-gradient-1-ref.html
 
 == zero-dimensions.html zero-dimensions-ref.html
 
 == evenodd-fill-sanity.html data:text/html,<body>Pass
 != evenodd-fill-1.html nonzero-fill-1.html
 == evenodd-fill-1.html evenodd-fill-ref.html
 == evenodd-fill-2.html evenodd-fill-ref.html
 == evenodd-fill-3.html nonzero-fill-2.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/745025-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+
+</head>
+<body>
+<!-- A 10x10 red image --><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFElEQVQYlWP4z8DwnxjMMKqQvgoBksPHOVp9kXEAAAAASUVORK5CYII=">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/745025-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-print">
+<head>
+  <script type="text/javascript">
+    window.onload = function() { 
+      var canvas = document.getElementById('canvas');
+      canvas.mozPrintCallback = function(obj) {
+        var ctx = obj.context;
+        ctx.fillStyle = 'rgb(255, 0, 0)';
+        ctx.fillRect(0, 0, 10, 10);
+        obj.done();
+      };
+      setTimeout(function() {
+        document.documentElement.className = "reftest-print" 
+      }, 0);
+    };
+  </script>
+</head>
+<body>
+  <canvas id="canvas" width="10px" height="10px"></canvas>
+</body>
+</html>
--- a/layout/reftests/printing/reftest.list
+++ b/layout/reftests/printing/reftest.list
@@ -16,8 +16,9 @@
 == 626395-1a.html 626395-1-ref.html
 == 626395-1b.html 626395-1-ref.html
 == 626395-2a.html 626395-2-ref.html
 == 626395-2b.html 626395-2-ref.html
 == 626395-2c.html 626395-2-ref.html
 == 626395-2d.html 626395-2-ref.html
 == 652178-1.html 652178-1-ref.html
 == 652178-1.html 652178-1-ref2.html
+== 745025-1.html 745025-1-ref.html
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -879,16 +879,17 @@ nsXULPopupManager::HidePopupCallback(nsI
   aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // send the popuphidden event synchronously. This event has no default behaviour.
   nsEventStatus status = nsEventStatus_eIgnore;
   nsMouseEvent event(true, NS_XUL_POPUP_HIDDEN, nullptr, nsMouseEvent::eReal);
   nsEventDispatcher::Dispatch(aPopup, aPopupFrame->PresContext(),
                               &event, nullptr, &status);
+  ENSURE_TRUE(weakFrame.IsAlive());
 
   // if there are more popups to close, look for the next one
   if (aNextPopup && aPopup != aLastPopup) {
     nsMenuChainItem* foundMenu = nullptr;
     nsMenuChainItem* item = mPopups;
     while (item) {
       if (item->Content() == aNextPopup) {
         foundMenu = item;
--- a/media/libcubeb/src/Makefile.in
+++ b/media/libcubeb/src/Makefile.in
@@ -18,16 +18,21 @@ endif
 
 ifeq ($(OS_TARGET),WINNT)
 CSRCS           = \
                 cubeb_winmm.c \
                 $(NULL)
 endif
 
 ifeq ($(OS_TARGET),Android)
+ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
+CSRCS         = \
+              cubeb_opensl.c \
+              $(NULL)
+endif
 # No Android implementation of libcubeb yet.
 else ifeq ($(OS_TARGET),Linux)
 CSRCS         = \
               cubeb_alsa.c \
               $(NULL)
 endif
 
 ifeq ($(OS_TARGET),Darwin)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright © 2012 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license.  See the
+ * accompanying file LICENSE for details.
+ */
+#undef NDEBUG
+#include "cubeb/cubeb.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <SLES/OpenSLES.h>
+
+struct cubeb {
+  SLObjectItf engObj;
+  SLEngineItf eng;
+};
+
+#define NBUFS 4
+
+struct cubeb_stream {
+  struct cubeb * context;
+  SLObjectItf playerObj;
+  SLPlayItf play;
+  SLBufferQueueItf bufq;
+  SLObjectItf outmixObj;
+  void *queuebuf[NBUFS];
+  int queuebuf_idx;
+  long queuebuf_len;
+  long bytespersec;
+  long framesize;
+
+  cubeb_data_callback data_callback;
+  cubeb_state_callback state_callback;
+  void * user_ptr;
+};
+
+static void
+bufferqueue_callback(SLBufferQueueItf caller, struct cubeb_stream *stm)
+{
+  void *buf = stm->queuebuf[stm->queuebuf_idx];
+
+  long written = stm->data_callback(stm, stm->user_ptr,
+                                    buf, stm->queuebuf_len / stm->framesize);
+  if (written <= 0)
+    return;
+
+  (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
+
+  stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
+  // XXX handle error
+}
+
+static void
+play_callback(SLPlayItf caller, struct cubeb_stream *stm, SLuint32 event)
+{
+  if (event & SL_PLAYEVENT_HEADSTALLED)
+    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+}
+
+int
+cubeb_init(cubeb ** context, char const * context_name)
+{
+  cubeb * ctx;
+
+  *context = NULL;
+
+  ctx = calloc(1, sizeof(*ctx));
+  assert(ctx);
+
+  const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
+
+  SLresult res;
+  res = slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
+  if (res != SL_RESULT_SUCCESS) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    free(ctx);
+    return CUBEB_ERROR;
+  }
+
+  res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_destroy(ctx);
+    return CUBEB_ERROR;
+  }
+
+  *context = ctx;
+
+  return CUBEB_OK;
+}
+
+char const *
+cubeb_get_backend_id(cubeb * ctx)
+{
+  return "opensl";
+}
+
+void
+cubeb_destroy(cubeb * ctx)
+{
+  (*ctx->engObj)->Destroy(ctx->engObj);
+  free(ctx);
+}
+
+int
+cubeb_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+                  cubeb_stream_params stream_params, unsigned int latency,
+                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+                  void * user_ptr)
+{
+  cubeb_stream * stm;
+
+  assert(ctx);
+
+  *stream = NULL;
+
+  if (stream_params.rate < 8000 || stream_params.rate > 48000 ||
+      stream_params.channels < 1 || stream_params.channels > 32 ||
+      latency < 1 || latency > 2000) {
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  SLDataFormat_PCM format;
+
+  format.formatType = SL_DATAFORMAT_PCM;
+  format.numChannels = stream_params.channels;
+  // samplesPerSec is in milliHertz
+  format.samplesPerSec = stream_params.rate * 1000;
+  format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
+  format.channelMask = stream_params.channels == 1 ?
+                       SL_SPEAKER_FRONT_CENTER :
+                       SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+
+  switch (stream_params.format) {
+  case CUBEB_SAMPLE_S16LE:
+    format.endianness = SL_BYTEORDER_LITTLEENDIAN;
+    break;
+  case CUBEB_SAMPLE_S16BE:
+    format.endianness = SL_BYTEORDER_BIGENDIAN;
+    break;
+  default:
+    return CUBEB_ERROR_INVALID_FORMAT;
+  }
+
+  stm = calloc(1, sizeof(*stm));
+  assert(stm);
+
+  stm->context = ctx;
+  stm->data_callback = data_callback;
+  stm->state_callback = state_callback;
+  stm->user_ptr = user_ptr;
+
+  stm->framesize = stream_params.channels * sizeof(int16_t);
+  stm->bytespersec = stream_params.rate * stm->framesize;
+  stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
+  stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
+  int i;
+  for (i = 0; i < NBUFS; i++) {
+    stm->queuebuf[i] = malloc(stm->queuebuf_len);
+    assert(stm->queuebuf[i]);
+  }
+
+  SLDataLocator_BufferQueue loc_bufq;
+  loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
+  loc_bufq.numBuffers = NBUFS;
+  SLDataSource source;
+  source.pLocator = &loc_bufq;
+  source.pFormat = &format;
+
+  SLresult res;
+  const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
+  const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
+  res = (*ctx->eng)->CreateOutputMix(ctx->eng, &stm->outmixObj, 1, idsom, reqom);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->outmixObj)->Realize(stm->outmixObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  SLDataLocator_OutputMix loc_outmix;
+  loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+  loc_outmix.outputMix = stm->outmixObj;
+  SLDataSink sink;
+  sink.pLocator = &loc_outmix;
+  sink.pFormat = NULL;
+
+  const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
+  const SLboolean req[] = {SL_BOOLEAN_TRUE};
+  res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
+                                       &source, &sink, 1, ids, req);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj, SL_IID_PLAY, &stm->play);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->playerObj)->GetInterface(stm->playerObj, SL_IID_BUFFERQUEUE,
+                                    &stm->bufq);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  res = (*stm->play)->SetCallbackEventsMask(stm->play, SL_PLAYEVENT_HEADSTALLED);
+  if (res != SL_RESULT_SUCCESS) {
+    cubeb_stream_destroy(stm);
+    return CUBEB_ERROR;
+  }
+
+  *stream = stm;
+
+  return CUBEB_OK;
+}
+
+void
+cubeb_stream_destroy(cubeb_stream * stm)
+{
+  if (stm->playerObj)
+    (*stm->playerObj)->Destroy(stm->playerObj);
+  if (stm->outmixObj)
+    (*stm->outmixObj)->Destroy(stm->outmixObj);
+  free(stm);
+}
+
+int
+cubeb_stream_start(cubeb_stream * stm)
+{
+  SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
+  bufferqueue_callback(NULL, stm);
+  return CUBEB_OK;
+}
+
+int
+cubeb_stream_stop(cubeb_stream * stm)
+{
+  SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+  return CUBEB_OK;
+}
+
+int
+cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
+{
+  SLmillisecond msec;
+  SLresult res = (*stm->play)->GetPosition(stm->play, &msec);
+  if (res != SL_RESULT_SUCCESS)
+    return CUBEB_ERROR;
+  *position = (stm->bytespersec * msec) / (1000 * stm->framesize);
+  return CUBEB_OK;
+}
+
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -485,41 +485,21 @@ pref("browser.search.param.yahoo-fr-ja",
 #endif
 
 /* prefs used by the update timer system (including blocklist pings) */
 pref("app.update.timerFirstInterval", 30000); // milliseconds
 pref("app.update.timerMinimumDelay", 30); // seconds
 
 #ifdef MOZ_UPDATER
 /* prefs used specifically for updating the app */
-pref("app.update.enabled", true);
-pref("app.update.auto", false);
+pref("app.update.enabled", false);
 pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
-pref("app.update.mode", 1);
-pref("app.update.silent", false);
-#ifdef MOZ_PKG_SPECIAL
-pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%-@MOZ_PKG_SPECIAL@/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
-#else
-pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
-#endif
-pref("app.update.promptWaitTime", 43200);
-pref("app.update.idletime", 60);
-pref("app.update.showInstalledUI", false);
-pref("app.update.incompatible.mode", 0);
-pref("app.update.download.backgroundInterval", 0);
 
-#ifdef MOZ_OFFICIAL_BRANDING
-pref("app.update.interval", 86400);
-pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/m/");
-pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/releases/");
-#else
-pref("app.update.interval", 3600);
-pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/mobile/");
-pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/");
-#endif
+// If you are looking for app.update.url, we no longer use it.
+// See mobile/android/base/UpdateServiceHelper.java.in
 #endif
 
 // replace newlines with spaces on paste into single-line text boxes
 pref("editor.singleLine.pasteNewlines", 2);
 
 // threshold where a tap becomes a drag, in 1/240" reference pixels
 // The names of the preferences are to be in sync with nsEventStateManager.cpp
 pref("ui.dragThresholdX", 25);
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -143,16 +143,22 @@
 
         <receiver android:name="NotificationHandler">
             <intent-filter>
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLICK" />
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLEAR" />
             </intent-filter>
         </receiver>
 
+        <receiver android:name="org.mozilla.gecko.GeckoUpdateReceiver">
+            <intent-filter>
+                <action android:name="@ANDROID_PACKAGE_NAME@.CHECK_UPDATE_RESULT" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="org.mozilla.gecko.GeckoMessageReceiver"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER">
             <intent-filter>
                   <action android:name="org.mozilla.gecko.INIT_PW"></action>
             </intent-filter>
         </receiver>
 
         <activity android:name="Restarter"
@@ -219,16 +225,23 @@
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.formhistory"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.FORMHISTORY_PROVIDER"
                   android:protectionLevel="signature"/>
 
         <provider android:name="@ANDROID_PACKAGE_NAME@.db.TabsProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.tabs"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
 
+        <service
+            android:exported="false"
+            android:name="org.mozilla.gecko.updater.UpdateService"
+            android:process="@MANGLED_ANDROID_PACKAGE_NAME@.UpdateService">
+        </service>
+
+
 #include ../sync/manifests/SyncAndroidManifest_services.xml.in
     </application>
 
     <permission android:name="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"
                 android:protectionLevel="signature"/>
 
     <permission android:name="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"
                 android:protectionLevel="signature"/>
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -11,16 +11,18 @@ import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PluginLayer;
 import org.mozilla.gecko.gfx.PointUtils;
 import org.mozilla.gecko.ui.PanZoomController;
 import org.mozilla.gecko.util.GeckoAsyncTask;
 import org.mozilla.gecko.util.GeckoBackgroundThread;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.GeckoEventResponder;
 import org.mozilla.gecko.GeckoAccessibility;
+import org.mozilla.gecko.updater.UpdateServiceHelper;
+import org.mozilla.gecko.updater.UpdateService;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
@@ -122,17 +124,16 @@ abstract public class GeckoApp
 
     public static final String ACTION_ALERT_CLICK   = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR   = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
     public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ACTION_ALERT_CALLBACK";
     public static final String ACTION_WEBAPP_PREFIX = "org.mozilla.gecko.WEBAPP";
     public static final String ACTION_DEBUG         = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_BOOKMARK      = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_LOAD          = "org.mozilla.gecko.LOAD";
-    public static final String ACTION_UPDATE        = "org.mozilla.gecko.UPDATE";
     public static final String ACTION_INIT_PW       = "org.mozilla.gecko.INIT_PW";
     public static final String SAVED_STATE_TITLE         = "title";
     public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
 
     public static final String PREFS_NAME          = "GeckoApp";
     public static final String PREFS_OOM_EXCEPTION = "OOMException";
     public static final String PREFS_WAS_STOPPED   = "wasStopped";
 
@@ -906,18 +907,16 @@ abstract public class GeckoApp
             } else if (event.equals("DOMFullScreen:Start")) {
                 mDOMFullScreen = true;
             } else if (event.equals("DOMFullScreen:Stop")) {
                 mDOMFullScreen = false;
             } else if (event.equals("Permissions:Data")) {
                 String host = message.getString("host");
                 JSONArray permissions = message.getJSONArray("permissions");
                 showSiteSettingsDialog(host, permissions);
-            } else if (event.equals("Update:Restart")) {
-                doRestart("org.mozilla.gecko.restart_update");
             } else if (event.equals("Tab:ViewportMetadata")) {
                 int tabId = message.getInt("tabID");
                 Tab tab = Tabs.getInstance().getTab(tabId);
                 if (tab == null)
                     return;
                 tab.setZoomConstraints(new ZoomConstraints(message));
                 // Sync up the layer view and the tab if the tab is currently displayed.
                 LayerView layerView = mLayerView;
@@ -996,16 +995,18 @@ abstract public class GeckoApp
                 String text = message.getString("text");
                 GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, "");
             } else if (event.equals("Share:Image")) {
                 String src = message.getString("url");
                 String type = message.getString("mime");
                 GeckoAppShell.shareImage(src, type);
             } else if (event.equals("Sanitize:ClearHistory")) {
                 handleClearHistory();
+            } else if (event.equals("Update:Check")) {
+                startService(new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class));
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
     public String getResponse() {
         Log.i(LOGTAG, "Return " + mCurrentResponse);
@@ -1483,21 +1484,16 @@ abstract public class GeckoApp
             }
             if (profileName != null || profilePath != null) {
                 mProfile = GeckoProfile.get(this, profileName, profilePath);
             }
         }
 
         BrowserDB.initialize(getProfile().getName());
 
-        if (ACTION_UPDATE.equals(action) || args != null && args.contains("-alert update-app")) {
-            Log.i(LOGTAG,"onCreate: Update request");
-            checkAndLaunchUpdate();
-        }
-
         String passedUri = null;
         String uri = getURIFromIntent(intent);
         if (uri != null && uri.length() > 0) {
             passedUri = uri;
         }
 
         if (mRestoreMode == GeckoAppShell.RESTORE_NONE && getProfile().shouldRestoreSession()) {
             Log.i(LOGTAG, "Restoring crash");
@@ -1582,31 +1578,31 @@ abstract public class GeckoApp
         registerEventListener("Gecko:Ready");
         registerEventListener("Toast:Show");
         registerEventListener("DOMFullScreen:Start");
         registerEventListener("DOMFullScreen:Stop");
         registerEventListener("ToggleChrome:Hide");
         registerEventListener("ToggleChrome:Show");
         registerEventListener("ToggleChrome:Focus");
         registerEventListener("Permissions:Data");
-        registerEventListener("Update:Restart");
         registerEventListener("Tab:HasTouchListener");
         registerEventListener("Tab:ViewportMetadata");
         registerEventListener("Session:StatePurged");
         registerEventListener("Bookmark:Insert");
         registerEventListener("Accessibility:Event");
         registerEventListener("Accessibility:Ready");
         registerEventListener("Shortcut:Remove");
         registerEventListener("WebApps:Open");
         registerEventListener("WebApps:Install");
         registerEventListener("WebApps:Uninstall");
         registerEventListener("DesktopMode:Changed");
         registerEventListener("Share:Text");
         registerEventListener("Share:Image");
         registerEventListener("Sanitize:ClearHistory");
+        registerEventListener("Update:Check");
 
         if (SmsManager.getInstance() != null) {
           SmsManager.getInstance().start();
         }
 
         mBatteryReceiver = new GeckoBatteryManager();
         mBatteryReceiver.registerFor(mAppContext);
 
@@ -1617,16 +1613,18 @@ abstract public class GeckoApp
 
         mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.start_handle),
                                            (TextSelectionHandle) findViewById(R.id.end_handle),
                                            GeckoAppShell.getEventDispatcher());
 
         GeckoNetworkManager.getInstance().init();
         GeckoNetworkManager.getInstance().start();
 
+        UpdateServiceHelper.registerForUpdates(this);
+
         GeckoScreenOrientationListener.getInstance().start();
 
         final GeckoApp self = this;
 
         GeckoAppShell.getHandler().postDelayed(new Runnable() {
             public void run() {
                 Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - pre checkLaunchState");
                 // Sync settings need Gecko to be loaded, so
@@ -2025,31 +2023,31 @@ abstract public class GeckoApp
         unregisterEventListener("Gecko:Ready");
         unregisterEventListener("Toast:Show");
         unregisterEventListener("DOMFullScreen:Start");
         unregisterEventListener("DOMFullScreen:Stop");
         unregisterEventListener("ToggleChrome:Hide");
         unregisterEventListener("ToggleChrome:Show");
         unregisterEventListener("ToggleChrome:Focus");
         unregisterEventListener("Permissions:Data");
-        unregisterEventListener("Update:Restart");
         unregisterEventListener("Tab:HasTouchListener");
         unregisterEventListener("Tab:ViewportMetadata");
         unregisterEventListener("Session:StatePurged");
         unregisterEventListener("Bookmark:Insert");
         unregisterEventListener("Accessibility:Event");
         unregisterEventListener("Accessibility:Ready");
         unregisterEventListener("Shortcut:Remove");
         unregisterEventListener("WebApps:Open");
         unregisterEventListener("WebApps:Install");
         unregisterEventListener("WebApps:Uninstall");
         unregisterEventListener("DesktopMode:Changed");
         unregisterEventListener("Share:Text");
         unregisterEventListener("Share:Image");
         unregisterEventListener("Sanitize:ClearHistory");
+        unregisterEventListener("Update:Check");
 
         deleteTempFiles();
 
         if (mLayerView != null)
             mLayerView.destroy();
         if (mDoorHangerPopup != null)
             mDoorHangerPopup.destroy();
         if (mFormAssistPopup != null)
@@ -2210,86 +2208,16 @@ abstract public class GeckoApp
         // Give the restart process time to start before we die
         GeckoAppShell.waitForAnotherGeckoProc();
     }
 
     public void handleNotification(String action, String alertName, String alertCookie) {
         GeckoAppShell.handleNotification(action, alertName, alertCookie);
     }
 
-    private void checkAndLaunchUpdate() {
-        Log.i(LOGTAG, "Checking for an update");
-
-        int statusCode = 8; // UNEXPECTED_ERROR
-        File baseUpdateDir = null;
-        if (Build.VERSION.SDK_INT >= 8)
-            baseUpdateDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
-        else
-            baseUpdateDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
-
-        File updateDir = new File(new File(baseUpdateDir, "updates"),"0");
-
-        File updateFile = new File(updateDir, "update.apk");
-        File statusFile = new File(updateDir, "update.status");
-
-        if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending"))
-            return;
-
-        if (!updateFile.exists())
-            return;
-
-        Log.i(LOGTAG, "Update is available!");
-
-        // Launch APK
-        File updateFileToRun = new File(updateDir, getPackageName() + "-update.apk");
-        try {
-            if (updateFile.renameTo(updateFileToRun)) {
-                String amCmd = "/system/bin/am start -a android.intent.action.VIEW " +
-                               "-n com.android.packageinstaller/.PackageInstallerActivity -d file://" +
-                               updateFileToRun.getPath();
-                Log.i(LOGTAG, amCmd);
-                Runtime.getRuntime().exec(amCmd);
-                statusCode = 0; // OK
-            } else {
-                Log.i(LOGTAG, "Cannot rename the update file!");
-                statusCode = 7; // WRITE_ERROR
-            }
-        } catch (Exception e) {
-            Log.i(LOGTAG, "error launching installer to update", e);
-        }
-
-        // Update the status file
-        String status = statusCode == 0 ? "succeeded\n" : "failed: "+ statusCode + "\n";
-
-        OutputStream outStream;
-        try {
-            byte[] buf = status.getBytes("UTF-8");
-            outStream = new FileOutputStream(statusFile);
-            outStream.write(buf, 0, buf.length);
-            outStream.close();
-        } catch (Exception e) {
-            Log.i(LOGTAG, "error writing status file", e);
-        }
-
-        if (statusCode == 0)
-            System.exit(0);
-    }
-
-    private String readUpdateStatus(File statusFile) {
-        String status = "";
-        try {
-            BufferedReader reader = new BufferedReader(new FileReader(statusFile));
-            status = reader.readLine();
-            reader.close();
-        } catch (Exception e) {
-            Log.i(LOGTAG, "error reading update status", e);
-        }
-        return status;
-    }
-
     private void checkMigrateProfile() {
         final File profileDir = getProfile().getDir();
         final long currentTime = SystemClock.uptimeMillis();
 
         if (profileDir != null) {
             final GeckoApp app = GeckoApp.mAppContext;
 
             GeckoAppShell.getHandler().post(new Runnable() {
@@ -2562,16 +2490,20 @@ abstract public class GeckoApp
             wl.acquire();
             mWakeLocks.put(topic, wl);
         } else if (!state.equals("locked-foreground") && wl != null) {
             wl.release();
             mWakeLocks.remove(topic);
         }
     }
 
+    public void notifyCheckUpdateResult(boolean result) {
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Update:CheckResult", result ? "true" : "false"));
+    }
+
     private void connectGeckoLayerClient() {
         mLayerView.getLayerClient().notifyGeckoReady();
 
         mLayerView.getTouchEventHandler().setOnTouchListener(new ContentTouchListener() {
             private PointF initialPoint = null;
 
             @Override
             public boolean onTouch(View view, MotionEvent event) {
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -2292,16 +2292,21 @@ public class GeckoAppShell
                 GeckoAppShell.onSurfaceTextureFrameAvailable(surfaceTexture, id);
             }
         });
     }
 
     public static void unregisterSurfaceTextureFrameListener(Object surfaceTexture) {
         ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(null);
     }
+
+    public static void notifyCheckUpdateResult(boolean result) {
+        if (GeckoApp.mAppContext != null)
+            GeckoApp.mAppContext.notifyCheckUpdateResult(result);
+    }
 }
 
 class ScreenshotHandler implements Runnable {
     public static final int SCREENSHOT_THUMBNAIL = 0;
     public static final int SCREENSHOT_CHECKERBOARD = 1;
 
     private static final String LOGTAG = "GeckoScreenshotHandler";
     private static final int BYTES_FOR_16BPP = 2;
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoUpdateReceiver.java
@@ -0,0 +1,22 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.updater.UpdateServiceHelper;
+
+import android.content.*;
+import android.net.*;
+
+public class GeckoUpdateReceiver
+    extends BroadcastReceiver
+{
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT.equals(intent.getAction())) {
+            GeckoAppShell.notifyCheckUpdateResult(intent.getBooleanExtra("result", false));
+        }
+    }
+}
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -159,16 +159,18 @@ FENNEC_JAVA_FILES = \
   gfx/VirtualLayer.java \
   ui/Axis.java \
   ui/PanZoomController.java \
   ui/PanZoomTarget.java \
   ui/SimpleScaleGestureDetector.java \
   ui/SubdocumentScrollHelper.java \
   GeckoNetworkManager.java \
   GeckoScreenOrientationListener.java \
+  UpdateService.java \
+  GeckoUpdateReceiver.java \
   $(MOZGLUE_JAVA_FILES) \
   $(UTIL_JAVA_FILES) \
   $(NULL)
 
 ifdef MOZ_WEBSMS_BACKEND
 FENNEC_JAVA_FILES += GeckoSmsManager.java
 endif
 
@@ -183,16 +185,17 @@ FENNEC_PP_JAVA_FILES = \
   Restarter.java \
   db/BrowserContract.java \
   db/BrowserProvider.java \
   db/PasswordsProvider.java \
   db/FormHistoryProvider.java \
   db/TabsProvider.java \
   db/GeckoProvider.java \
   SmsManager.java \
+  UpdateServiceHelper.java \
   $(NULL)
 
 FENNEC_PP_XML_FILES = \
   res/layout/abouthome_content.xml \
   res/layout/browser_toolbar.xml \
   res/layout/browser_toolbar_menu.xml \
   res/layout-land-v14/browser_toolbar.xml \
   res/layout-land-v14/browser_toolbar_menu.xml \
@@ -214,16 +217,18 @@ FENNEC_PP_XML_FILES = \
 
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
 
+MOZ_APP_BUILDID=$(shell cat $(DEPTH)/config/buildid)
+
 ifeq (,$(ANDROID_VERSION_CODE))
 ifeq ($(MIN_CPU_VERSION),7)
 ANDROID_VERSION_CODE=$(shell cat $(DEPTH)/config/buildid | cut -c1-10)
 else
 # decrement the version code by 1 for armv6 builds so armv7 builds will win any compatability ties
 ANDROID_VERSION_CODE=$(shell echo $$((`cat $(DEPTH)/config/buildid | cut -c1-10` - 1)))
 endif
 endif
@@ -238,16 +243,20 @@ DEFINES += \
   -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
   -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
   -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) \
   -DMOZ_MIN_CPU_VERSION=$(MIN_CPU_VERSION) \
   -DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
   -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
   -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
   -DUA_BUILDID=$(UA_BUILDID) \
+  -DMOZ_APP_BASENAME=$(MOZ_APP_BASENAME) \
+  -DMOZ_APP_BUILDID=$(MOZ_APP_BUILDID) \
+  -DMOZ_APP_ABI=$(TARGET_XPCOM_ABI) \
+  -DMOZ_UPDATER=$(MOZ_UPDATER) \
   $(NULL)
 
 ifdef MOZ_LINKER_EXTRACT
 DEFINES += -DMOZ_LINKER_EXTRACT=1
 endif
 
 GARBAGE += \
   AndroidManifest.xml  \
--- a/mobile/android/base/Restarter.java.in
+++ b/mobile/android/base/Restarter.java.in
@@ -38,20 +38,17 @@ public class Restarter extends Activity 
                         Thread.sleep(100);
                     } catch (InterruptedException ie) {}
                 }
             }
         } catch (Exception e) {
             Log.i(LOGTAG, e.toString());
         }
         try {
-            String action = "org.mozilla.gecko.restart_update".equals(getIntent().getAction()) ?
-                            App.ACTION_UPDATE : Intent.ACTION_MAIN;
-
-            Intent intent = new Intent(action);
+            Intent intent = new Intent(Intent.ACTION_MAIN);
             intent.setClassName("@ANDROID_PACKAGE_NAME@",
                                 "@ANDROID_PACKAGE_NAME@.App");
             Bundle b = getIntent().getExtras();
             if (b != null)
                 intent.putExtras(b);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
             Log.i(LOGTAG, intent.toString());
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/UpdateService.java
@@ -0,0 +1,638 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko.updater;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.GeckoApp;
+
+import org.mozilla.apache.commons.codec.binary.Hex;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+
+import android.util.Log;
+
+import android.widget.RemoteViews;
+
+import java.net.URL;
+import java.net.URLConnection;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.security.MessageDigest;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Random;
+import java.util.TimeZone;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+
+public class UpdateService extends IntentService {
+    private static final int BUFSIZE = 8192;
+    private static final int NOTIFICATION_ID = 0x3e40ddbd;
+
+    private static final String LOGTAG = "UpdateService";
+
+    private static final int INTERVAL_LONG = 86400000; // in milliseconds
+    private static final int INTERVAL_SHORT = 14400000; // again, in milliseconds
+    private static final int INTERVAL_RETRY = 3600000;
+
+    private static final String PREFS_NAME = "UpdateService";
+    private static final String KEY_LAST_BUILDID = "UpdateService.lastBuildID";
+    private static final String KEY_LAST_HASH_FUNCTION = "UpdateService.lastHashFunction";
+    private static final String KEY_LAST_HASH_VALUE = "UpdateService.lastHashValue";
+    private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
+
+    private SharedPreferences mPrefs;
+
+    private NotificationManager mNotificationManager;
+    private ConnectivityManager mConnectivityManager;
+
+    private boolean mDownloading;
+    private boolean mApplyImmediately;
+
+    public UpdateService() {
+        super("updater");
+    }
+
+    @Override
+    public void onCreate () {
+        super.onCreate();
+        
+        mPrefs = getSharedPreferences(PREFS_NAME, 0);
+        mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+        mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    public synchronized int onStartCommand (Intent intent, int flags, int startId) {
+        // If we are busy doing a download, the new Intent here would normally be queued for
+        // execution once that is done. In this case, however, we want to flip the boolean
+        // while that is running, so handle that now.
+        if (mDownloading && UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
+            Log.i(LOGTAG, "will apply update when download finished");
+
+            mApplyImmediately = true;
+            showDownloadNotification();
+        } else {
+            super.onStartCommand(intent, flags, startId);
+        }
+
+        return Service.START_REDELIVER_INTENT;
+    }
+
+    @Override
+    protected void onHandleIntent (Intent intent) {
+        if (UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES.equals(intent.getAction())) {
+            registerForUpdates(false);
+        } if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) {
+            startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0));
+        } else if (UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
+            applyUpdate(intent.getStringExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME));
+        }
+    }
+
+    private static boolean hasFlag(int flags, int flag) {
+        return (flags & flag) == flag;
+    }
+
+    private void sendCheckUpdateResult(boolean result) {
+        Intent resultIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT);
+        resultIntent.putExtra("result", result);
+        sendBroadcast(resultIntent);
+    }
+
+    private int getUpdateInterval(boolean isRetry) {
+        int interval;
+        if (isRetry) {
+            interval = INTERVAL_RETRY;
+        } else if (UpdateServiceHelper.UPDATE_CHANNEL.equals("nightly") ||
+                   UpdateServiceHelper.UPDATE_CHANNEL.equals("aurora")) {
+            interval = INTERVAL_SHORT;
+        } else {
+            interval = INTERVAL_LONG;
+        }
+
+        return interval;
+    }
+
+    private void registerForUpdates(boolean isRetry) {
+        Calendar lastAttempt = getLastAttemptDate();
+        Calendar now = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+
+        int interval = getUpdateInterval(isRetry);
+
+        if (lastAttempt == null || (now.getTimeInMillis() - lastAttempt.getTimeInMillis()) > interval) {
+            // We've either never attempted an update, or we are passed the desired
+            // time. Start an update now.
+            Log.i(LOGTAG, "no update has ever been attempted, checking now");
+            startUpdate(0);
+            return;
+        }
+
+        AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+        if (manager == null)
+            return;
+
+        PendingIntent pending = PendingIntent.getService(this, 0, new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class), 0);
+        manager.cancel(pending);
+
+        lastAttempt.setTimeInMillis(lastAttempt.getTimeInMillis() + interval);
+        Log.i(LOGTAG, "next update will be at: " + lastAttempt.getTime());
+
+        manager.set(AlarmManager.RTC_WAKEUP, lastAttempt.getTimeInMillis(), pending);
+    }
+
+    private void startUpdate(int flags) {
+        setLastAttemptDate();
+
+        NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
+        if (netInfo == null || !netInfo.isConnected()) {
+            Log.i(LOGTAG, "not connected to the network");
+            registerForUpdates(true);
+            sendCheckUpdateResult(false);
+            return;
+        }
+
+        registerForUpdates(false);
+
+        UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
+        boolean haveUpdate = (info != null);
+        sendCheckUpdateResult(haveUpdate);
+
+        if (!haveUpdate) {
+            Log.i(LOGTAG, "no update available");
+            return;
+        }
+
+        Log.i(LOGTAG, "update available, buildID = " + info.buildID);
+        
+        int connectionType = netInfo.getType();
+        if (!hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) &&
+            connectionType != ConnectivityManager.TYPE_WIFI &&
+            connectionType != ConnectivityManager.TYPE_ETHERNET) {
+            Log.i(LOGTAG, "not connected via wifi or ethernet");
+
+            // We aren't autodownloading here, so prompt to start the update
+            Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_start_ticker), System.currentTimeMillis());
+
+            Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
+            notificationIntent.setClass(this, UpdateService.class);
+            notificationIntent.putExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD);
+
+            PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            notification.flags = Notification.FLAG_AUTO_CANCEL;
+
+            notification.setLatestEventInfo(this, getResources().getString(R.string.updater_start_title),
+                                            getResources().getString(R.string.updater_start_select),
+                                            contentIntent);
+
+            mNotificationManager.notify(NOTIFICATION_ID, notification);
+
+            return;
+        }
+
+        File pkg = downloadUpdatePackage(info, hasFlag(flags, UpdateServiceHelper.FLAG_OVERWRITE_EXISTING));
+        if (pkg == null)
+            return;
+
+        Log.i(LOGTAG, "have update package at " + pkg);
+
+        saveUpdateInfo(info);
+
+        // If we have root, we always apply the update immediately because it happens in the background
+        if (mApplyImmediately || checkRoot()) {
+            applyUpdate(pkg);
+        } else {
+            // Prompt to apply the update
+            Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_apply_ticker), System.currentTimeMillis());
+
+            Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
+            notificationIntent.setClass(this, UpdateService.class);
+            notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, pkg.getAbsolutePath());
+
+            PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            notification.flags = Notification.FLAG_AUTO_CANCEL;
+
+            notification.setLatestEventInfo(this, getResources().getString(R.string.updater_apply_title),
+                                            getResources().getString(R.string.updater_apply_select),
+                                            contentIntent);
+
+            mNotificationManager.notify(NOTIFICATION_ID, notification);
+        }
+    }
+
+    private UpdateInfo findUpdate(boolean force) {
+        try {
+            URL url = UpdateServiceHelper.getUpdateUrl(force);
+
+            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+            Document dom = builder.parse(url.openConnection().getInputStream());
+
+            NodeList nodes = dom.getElementsByTagName("update");
+            if (nodes == null || nodes.getLength() == 0)
+                return null;
+
+            Node updateNode = nodes.item(0);
+            Node buildIdNode = updateNode.getAttributes().getNamedItem("buildID");
+            if (buildIdNode == null)
+                return null;
+
+            nodes = dom.getElementsByTagName("patch");
+            if (nodes == null || nodes.getLength() == 0)
+                return null;
+
+            Node patchNode = nodes.item(0);
+            Node urlNode = patchNode.getAttributes().getNamedItem("URL");
+            Node hashFunctionNode = patchNode.getAttributes().getNamedItem("hashFunction");
+            Node hashValueNode = patchNode.getAttributes().getNamedItem("hashValue");
+            Node sizeNode = patchNode.getAttributes().getNamedItem("size");
+
+            if (urlNode == null || hashFunctionNode == null ||
+                hashValueNode == null || sizeNode == null) {
+                return null;
+            }   
+
+            // Fill in UpdateInfo from the XML data
+            UpdateInfo info = new UpdateInfo();
+            info.url = new URL(urlNode.getTextContent());
+            info.buildID = buildIdNode.getTextContent();
+            info.hashFunction = hashFunctionNode.getTextContent();
+            info.hashValue = hashValueNode.getTextContent();
+
+            try {
+                info.size = Integer.parseInt(sizeNode.getTextContent());
+            } catch (NumberFormatException e) {
+                Log.e(LOGTAG, "Failed to find APK size: ", e);
+                return null;
+            }
+
+            // Make sure we have all the stuff we need to apply the update
+            if (!info.isValid()) {
+                Log.e(LOGTAG, "missing some required update information, have: " + info);
+                return null;
+            }
+
+            return info;
+        } catch (Exception e) {
+            Log.e(LOGTAG, "failed to check for update: ", e);
+            return null;
+        }
+    }
+
+    private MessageDigest createMessageDigest(String hashFunction) {
+        String javaHashFunction = null;
+
+        if ("sha512".equals(hashFunction)) {
+            javaHashFunction = "SHA-512";
+        } else {
+            Log.e(LOGTAG, "Unhandled hash function: " + hashFunction);
+            return null;
+        }
+
+        try {
+            return MessageDigest.getInstance(javaHashFunction);
+        } catch (java.security.NoSuchAlgorithmException e) {
+            Log.e(LOGTAG, "Couldn't find algorithm " + javaHashFunction, e);
+            return null;
+        }
+    }
+
+    private void showDownloadNotification() {
+        showDownloadNotification(null);
+    }
+
+    private void showDownloadNotification(File downloadFile) {
+        Notification notification = new Notification(android.R.drawable.stat_sys_download, getResources().getString(R.string.updater_downloading_ticker), System.currentTimeMillis());
+
+        Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
+        notificationIntent.setClass(this, UpdateService.class);
+
+        if (downloadFile != null)
+            notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, downloadFile.getAbsolutePath());
+
+        PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title),
+                                        mApplyImmediately ? getResources().getString(R.string.updater_downloading_willapply) :
+                                            getResources().getString(R.string.updater_downloading_select),
+                                        contentIntent);
+        
+        mNotificationManager.notify(NOTIFICATION_ID, notification);
+    }
+
+    private void showDownloadFailure() {
+        Notification notification = new Notification(android.R.drawable.stat_sys_warning, getResources().getString(R.string.updater_downloading_ticker_failed), System.currentTimeMillis());
+
+        Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
+        notificationIntent.setClass(this, UpdateService.class);
+
+        PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title),
+                                        getResources().getString(R.string.updater_downloading_retry),
+                                        contentIntent);
+        
+        mNotificationManager.notify(NOTIFICATION_ID, notification);
+    }
+
+    private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
+        File downloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), new File(info.url.getFile()).getName());
+
+        if (!overwriteExisting && info.buildID.equals(getLastBuildID()) && downloadFile.exists()) {
+            // The last saved buildID is the same as the one for the current update. We also have a file
+            // already downloaded, so it's probably the package we want. Verify it to be sure and just
+            // return that if it matches.
+
+            if (verifyDownloadedPackage(downloadFile)) {
+                Log.i(LOGTAG, "using existing update package");
+                return downloadFile;
+            } else {
+                // Didn't match, so we're going to download a new one.
+                downloadFile.delete();
+            }
+        }
+
+        Log.i(LOGTAG, "downloading update package");
+
+        OutputStream output = null;
+        InputStream input = null;
+
+        mDownloading = true;
+        showDownloadNotification(downloadFile);
+
+        try {
+            URLConnection conn = info.url.openConnection();
+            int length = conn.getContentLength();
+
+            output = new BufferedOutputStream(new FileOutputStream(downloadFile));
+            input = new BufferedInputStream(conn.getInputStream());
+
+            byte[] buf = new byte[BUFSIZE];
+            int len = 0;
+
+            int bytesRead = 0;
+            float lastPercent = 0.0f;
+
+            while ((len = input.read(buf, 0, BUFSIZE)) > 0) {
+                output.write(buf, 0, len);
+                bytesRead += len;
+            }
+
+            Log.i(LOGTAG, "completed update download!");
+
+            mNotificationManager.cancel(NOTIFICATION_ID);
+
+            return downloadFile;
+        } catch (Exception e) {
+            downloadFile.delete();
+            showDownloadFailure();
+
+            Log.e(LOGTAG, "failed to download update: ", e);
+            return null;
+        } finally {
+            try {
+                if (input != null)
+                    input.close();
+            } catch (java.io.IOException e) {}
+
+            try {
+                if (output != null)
+                    output.close();
+            } catch (java.io.IOException e) {}
+
+            mDownloading = false;
+        }
+    }
+
+    private boolean verifyDownloadedPackage(File updateFile) {
+        MessageDigest digest = createMessageDigest(getLastHashFunction());
+        if (digest == null)
+            return false;
+
+        InputStream input = null;
+
+        try {
+            input = new BufferedInputStream(new FileInputStream(updateFile));
+
+            byte[] buf = new byte[BUFSIZE];
+            int len;
+            while ((len = input.read(buf, 0, BUFSIZE)) > 0) {
+                digest.update(buf, 0, len);
+            }
+        } catch (java.io.IOException e) {
+            Log.e(LOGTAG, "Failed to verify update package: ", e);
+            return false;
+        } finally {
+            try {
+                if (input != null)
+                    input.close();
+            } catch(java.io.IOException e) {}
+        }
+
+        String hex = Hex.encodeHexString(digest.digest());
+        if (!hex.equals(getLastHashValue())) {
+            Log.e(LOGTAG, "Package hash does not match");
+            return false;
+        }
+
+        return true;
+    }
+
+    private void applyUpdate(String updatePath) {
+        applyUpdate(new File(updatePath));
+    }
+
+    private void applyUpdate(File updateFile) {
+        mApplyImmediately = false;
+
+        if (!updateFile.exists())
+            return;
+
+        Log.i(LOGTAG, "Verifying package: " + updateFile);
+
+        if (!verifyDownloadedPackage(updateFile)) {
+            Log.e(LOGTAG, "Not installing update, failed verification");
+            return;
+        }
+
+        if (checkRoot())
+            applyUpdateWithRoot(updateFile);
+        else
+            applyUpdateWithActivity(updateFile);
+    }
+
+    private void applyUpdateWithRoot(File updateFile) {
+        mNotificationManager.cancel(NOTIFICATION_ID);
+
+        Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_installing_ticker), System.currentTimeMillis());
+        notification.flags = Notification.FLAG_NO_CLEAR;
+
+        Intent notificationIntent = new Intent("org.mozilla.gecko.ACTION_NOOP");
+        notificationIntent.setClass(this, UpdateService.class);
+
+        PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, 0);
+        notification.flags = Notification.FLAG_NO_CLEAR;
+
+        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
+                                        getResources().getString(R.string.updater_installing_text),
+                                        contentIntent);
+
+        mNotificationManager.notify(NOTIFICATION_ID, notification);
+
+        int result = runAsRoot("pm install " + updateFile.getAbsolutePath());
+        Log.i(LOGTAG, "install result = " + result);
+
+        updateFile.delete();
+
+        int tickerText = result == 0 ? R.string.updater_installing_ticker_success : R.string.updater_installing_ticker_fail;
+        int contentText = result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail;
+
+        mNotificationManager.cancel(NOTIFICATION_ID);
+
+        notification = new Notification(R.drawable.icon, getResources().getString(tickerText), System.currentTimeMillis());
+        notification.flags = Notification.FLAG_NO_CLEAR;
+
+        notificationIntent = new Intent(Intent.ACTION_MAIN);
+        notificationIntent.setClassName(getPackageName(), getPackageName() + ".App");
+        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
+        notification.flags = Notification.FLAG_NO_CLEAR;
+
+        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
+                                        getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail),
+                                        contentIntent);
+
+        mNotificationManager.notify(NOTIFICATION_ID, notification);
+
+
+        notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
+                                        getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail),
+                                        contentIntent);
+        notification.flags = Notification.FLAG_AUTO_CANCEL;
+        mNotificationManager.notify(NOTIFICATION_ID, notification);
+    }
+
+    private void applyUpdateWithActivity(File updateFile) {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive");
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private String getLastBuildID() {
+        return mPrefs.getString(KEY_LAST_BUILDID, null);
+    }
+
+    private String getLastHashFunction() {
+        return mPrefs.getString(KEY_LAST_HASH_FUNCTION, null);
+    }
+
+    private String getLastHashValue() {
+        return mPrefs.getString(KEY_LAST_HASH_VALUE, null);
+    }
+
+    private Calendar getLastAttemptDate() {
+        long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1);
+        if (lastAttempt < 0)
+            return null;
+
+        GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+        cal.setTimeInMillis(lastAttempt);
+        return cal;
+    }
+
+    private void setLastAttemptDate() {
+        SharedPreferences.Editor editor = mPrefs.edit();
+        editor.putLong(KEY_LAST_ATTEMPT_DATE, System.currentTimeMillis());
+        editor.commit();
+    }
+
+    private void saveUpdateInfo(UpdateInfo info) {
+        SharedPreferences.Editor editor = mPrefs.edit();
+        editor.putString(KEY_LAST_BUILDID, info.buildID);
+        editor.putString(KEY_LAST_HASH_FUNCTION, info.hashFunction);
+        editor.putString(KEY_LAST_HASH_VALUE, info.hashValue);
+        editor.commit();
+    }
+
+    private int runAsRoot(String command) {
+        Process p = null;
+        try {
+            p = Runtime.getRuntime().exec("su");
+            OutputStream output = p.getOutputStream();
+            output.write(command.getBytes());
+            output.write(new String("; exit\n").getBytes());
+            output.flush();
+            p.waitFor();
+
+            return p.exitValue();
+        } catch (Exception e) {
+            return -1;
+        } finally {
+            if (p != null)
+                p.destroy();
+        }
+    }
+
+    private boolean checkRoot() {
+        return runAsRoot("echo woooooo") == 0;
+    }
+
+    private class UpdateInfo {
+        public URL url;
+        public String buildID;
+        public String hashFunction;
+        public String hashValue;
+        public int size;
+
+        private boolean isNonEmpty(String s) {
+            return s != null && s.length() > 0;
+        }
+
+        public boolean isValid() {
+            return url != null && isNonEmpty(buildID) &&
+                isNonEmpty(hashFunction) && isNonEmpty(hashValue) && size > 0;
+        }
+
+        @Override
+        public String toString() {
+            return "url = " + url + ", buildID = " + buildID + ", hashFunction = " + hashFunction + ", hashValue = " + hashValue + ", size = " + size;
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/UpdateServiceHelper.java.in
@@ -0,0 +1,79 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+#filter substitution
+
+package org.mozilla.gecko.updater;
+
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Build;
+
+import android.util.Log;
+
+import java.net.URL;
+
+import java.util.Locale;
+
+public class UpdateServiceHelper {
+    public static final String ACTION_REGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.REGISTER_FOR_UPDATES";
+    public static final String ACTION_UNREGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.UNREGISTER_FOR_UPDATES";
+    public static final String ACTION_CHECK_FOR_UPDATE = "@ANDROID_PACKAGE_NAME@.CHECK_FOR_UPDATE";
+    public static final String ACTION_CHECK_UPDATE_RESULT = "@ANDROID_PACKAGE_NAME@.CHECK_UPDATE_RESULT";
+    public static final String ACTION_APPLY_UPDATE = "@ANDROID_PACKAGE_NAME@.APPLY_UPDATE";
+
+    // Flags for ACTION_CHECK_FOR_UPDATE
+    public static final int FLAG_FORCE_DOWNLOAD = 1;
+    public static final int FLAG_OVERWRITE_EXISTING = 1 << 1;
+    public static final int FLAG_REINSTALL = 1 << 2;
+    public static final int FLAG_RETRY = 1 << 3;
+
+    // Name of the Intent extra that holds the flags for ACTION_CHECK_FOR_UPDATE
+    public static final String EXTRA_UPDATE_FLAGS_NAME = "updateFlags";
+
+    // Name of the Intent extra that holds the APK path, used with ACTION_APPLY_UPDATE
+    public static final String EXTRA_PACKAGE_PATH_NAME = "packagePath";
+
+    public static final String UPDATE_CHANNEL = "@MOZ_UPDATE_CHANNEL@";
+
+    private static final String LOGTAG = "UpdateServiceHelper";
+    private static final String BUILDID = "@MOZ_APP_BUILDID@";
+
+#ifdef MOZ_PKG_SPECIAL
+    private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@-@MOZ_PKG_SPECIAL@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml";
+#else
+    private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml";  
+#endif
+    
+    public static URL getUpdateUrl(boolean force) {
+        Locale locale = Locale.getDefault();
+        String url = UPDATE_URL.replace("%LOCALE%", locale.getLanguage() + "-" + locale.getCountry()).
+            replace("%OS_VERSION%", Build.VERSION.RELEASE).
+            replace("%BUILDID%", force ? "0" : BUILDID);
+
+        try {
+            return new URL(url);
+        } catch (java.net.MalformedURLException e) {
+            Log.e(LOGTAG, "Failed to create update url: ", e);
+            return null;
+        }
+    }
+
+    public static boolean isUpdaterEnabled() {
+#ifdef MOZ_UPDATER
+        return true;
+#else
+        return false;
+#endif
+    }
+
+    public static void registerForUpdates(Context context) {
+        if (!isUpdaterEnabled())
+            return;
+
+        context.startService(new Intent(UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES, null, context, UpdateService.class));
+    }
+}
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -339,17 +339,17 @@ public class GeckoLayerClient
                 break;
             }
 
             post(new Runnable() {
                 public void run() {
                     mGeckoViewport = newMetrics;
                 }
             });
-            setViewportMetrics(newMetrics);
+            setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE);
             mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null);
         }
         mReturnDisplayPort = mDisplayPort;
     }
 
     /** Implementation of GeckoEventResponder/GeckoEventListener. */
     public void handleMessage(String event, JSONObject message) {
         try {
@@ -618,19 +618,23 @@ public class GeckoLayerClient
             adjustViewport(displayPort);
         }
     }
 
     /** Implementation of PanZoomTarget
      * You must hold the monitor while calling this.
      */
     public void setViewportMetrics(ViewportMetrics viewport) {
+        setViewportMetrics(viewport, true);
+    }
+
+    private void setViewportMetrics(ViewportMetrics viewport, boolean notifyGecko) {
         mViewportMetrics = new ImmutableViewportMetrics(viewport);
         mView.requestRender();
-        if (mGeckoIsReady) {
+        if (notifyGecko && mGeckoIsReady) {
             geometryChanged();
         }
     }
 
     /** Implementation of PanZoomTarget */
     public void setForceRedraw() {
         mForceRedraw = true;
         if (mGeckoIsReady) {
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -219,8 +219,32 @@ just addresses the organization to follo
                                       from Android">
 <!ENTITY bookmarkhistory_import_bookmarks "Importing bookmarks
                                            from Android">
 <!ENTITY bookmarkhistory_import_history "Importing history
                                          from Android">
 <!ENTITY bookmarkhistory_import_wait "Please wait...">
 
 <!ENTITY webapp_generic_name "App">
+
+<!-- Updater notifications -->
+<!ENTITY updater_start_title "&brandShortName;">
+<!ENTITY updater_start_ticker "&brandShortName; update available&#8230;">
+<!ENTITY updater_start_select "Select to download update.">
+
+<!ENTITY updater_downloading_title "Downloading &brandShortName;">
+<!ENTITY updater_downloading_ticker "Downloading &brandShortName; update&#8230;">
+<!ENTITY updater_downloading_ticker_failed "Failed to download &brandShortName; update&#8230;">
+<!ENTITY updater_downloading_select "Select to apply update when complete.">
+<!ENTITY updater_downloading_retry "Select to retry update download.">
+<!ENTITY updater_downloading_willapply "Waiting for download to complete.">
+
+<!ENTITY updater_apply_title "&brandShortName;">
+<!ENTITY updater_apply_ticker "&brandShortName; update available&#8230;">
+<!ENTITY updater_apply_select "Select to apply downloaded update.">
+
+<!ENTITY updater_installing_title "&brandShortName;">
+<!ENTITY updater_installing_ticker "Updating &brandShortName;&#8230;">
+<!ENTITY updater_installing_ticker_success "Successfully updated &brandShortName;">
+<!ENTITY updater_installing_ticker_fail "Failed to update &brandShortName;">
+<!ENTITY updater_installing_text "Installing update&#8230;">
+<!ENTITY updater_installing_text_success "Succesfully updated.">
+<!ENTITY updater_installing_text_fail "Failed to install update.">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -205,9 +205,34 @@
   <!-- Bookmark import/export -->
   <string name="bookmarkhistory_button_import">&bookmarkhistory_button_import;</string>
   <string name="bookmarkhistory_import_both">&bookmarkhistory_import_both;</string>
   <string name="bookmarkhistory_import_bookmarks">&bookmarkhistory_import_bookmarks;</string>
   <string name="bookmarkhistory_import_history">&bookmarkhistory_import_history;</string>
   <string name="bookmarkhistory_import_wait">&bookmarkhistory_import_wait;</string>
 
   <string name="webapp_generic_name">&webapp_generic_name;</string>
+
+  <!-- Updater notifications -->
+  <string name="updater_start_title">&updater_start_title;</string>
+  <string name="updater_start_ticker">&updater_start_ticker;</string>
+  <string name="updater_start_select">&updater_start_select;</string>
+
+  <string name="updater_downloading_title">&updater_downloading_title;</string>
+  <string name="updater_downloading_ticker">&updater_downloading_ticker;</string>
+  <string name="updater_downloading_ticker_failed">&updater_downloading_ticker_failed;</string>
+  <string name="updater_downloading_select">&updater_downloading_select;</string>
+  <string name="updater_downloading_retry">&updater_downloading_retry;</string>
+  <string name="updater_downloading_willapply">&updater_downloading_willapply;</string>
+  
+  <string name="updater_apply_title">&updater_apply_title;</string>
+  <string name="updater_apply_ticker">&updater_apply_ticker;</string>
+  <string name="updater_apply_select">&updater_apply_select;</string>
+
+  <string name="updater_installing_title">&updater_installing_title;</string>
+  <string name="updater_installing_ticker">&updater_installing_ticker;</string>
+  <string name="updater_installing_ticker_success">&updater_installing_ticker_success;</string>
+  <string name="updater_installing_ticker_fail">&updater_installing_ticker_fail;</string>
+  <string name="updater_installing_text">&updater_installing_text;</string>
+  <string name="updater_installing_text_success">&updater_installing_text_success;</string>
+  <string name="updater_installing_text_fail">&updater_installing_text_fail;</string>
+
 </resources>
--- a/mobile/android/chrome/content/Readability.js
+++ b/mobile/android/chrome/content/Readability.js
@@ -114,19 +114,20 @@ Readability.prototype = {
         return uri;
 
       // Scheme-rooted relative URI.
       if (uri.substr(0, 2) == "//")
         return scheme + "://" + uri.substr(2);
 
       // Prepath-rooted relative URI.
       if (uri[0] == "/")
-        return prePath + "/" + uri;
+        return prePath + uri;
 
-      // Standard relative URI; add entire path.
+      // Standard relative URI; add entire path. pathBase already includes a
+      // trailing "/".
       return pathBase + uri;
     }
 
     function convertRelativeURIs(tagName, propName) {
       let elems = articleContent.getElementsByTagName(tagName);
       for (let i = elems.length; --i >= 0;) {
         let elem = elems[i];
         let relativeURI = elem.getAttribute(propName);
--- a/mobile/android/chrome/content/about.xhtml
+++ b/mobile/android/chrome/content/about.xhtml
@@ -96,90 +96,39 @@
         });
       } catch (ex) {}
 
 #ifdef MOZ_UPDATER
       let Updater = {
         isChecking: false,
         update: null,
 
-        get updateEnabled() {
-          try {
-            return Services.prefs.getBoolPref("app.update.enabled");
-          }
-          catch (e) { }
-          return true; // Mobile default is true
-        },
-
-        startUpdate: function() {
-          if (!this.update)
-            this.update = this.um.activeUpdate;
-
-          this.aus.pauseDownload();
-
-          let updateTimerCallback = this.aus.QueryInterface(Ci.nsITimerCallback);
-          updateTimerCallback.notify(null);
-        }
-      };
-
-      XPCOMUtils.defineLazyServiceGetter(Updater, "aus",
-                                         "@mozilla.org/updates/update-service;1",
-                                         "nsIApplicationUpdateService");
-      XPCOMUtils.defineLazyServiceGetter(Updater, "checker",
-                                         "@mozilla.org/updates/update-checker;1",
-                                         "nsIUpdateChecker");
-      XPCOMUtils.defineLazyServiceGetter(Updater, "um",
-                                         "@mozilla.org/updates/update-manager;1",
-                                         "nsIUpdateManager");
-
-      let UpdateCheckListener = {
-        onProgress: function(aRequest, aPosition, aTotalSize) {
+        init: function() {
+          Services.obs.addObserver(this, "Update:CheckResult", false);
         },
 
-        onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
-          Updater.isChecking = false;
-
-          Updater.update = Updater.aus.selectUpdate(aUpdates, aUpdates.length);
-          if (!Updater.update || !Updater.aus.canApplyUpdates) {
-            showUpdateMessage(false);
-            return;
-          }
-
-          if (!Updater.update.appVersion || Services.vc.compare(Updater.update.appVersion, Services.appinfo.version) < 0) {
-            showUpdateMessage(false);
-            return;
+        observe: function(aSubject, aTopic, aData) {
+          if (aTopic == "Update:CheckResult") {
+            showUpdateMessage(aData == "true");
           }
-
-          showUpdateMessage(true);
-          Updater.startUpdate();
         },
-
-        onError: function(aRequest, aUpdate) {
-          // Errors in the update check are treated as no updates found. If the
-          // update check fails repeatedly without a success the user will be
-          // notified with the normal app update user interface so this is safe.
-          Updater.isChecking = false;
-          showUpdateMessage(false);
-        },
-
-        QueryInterface: function(aIID) {
-          if (!aIID.equals(Ci.nsIUpdateCheckListener) && !aIID.equals(Ci.nsISupports))
-            throw Cr.NS_ERROR_NO_INTERFACE;
-          return this;
-        }
       };
 
-      if (!Updater.updateEnabled)
-        document.getElementById("updateBox").style.display = "none";
+      Updater.init();
 
       function checkForUpdates() {
         Updater.isChecking = true;
         showCheckingMessage();
 
-        Updater.checker.checkForUpdates(UpdateCheckListener, true);
+        let bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
+        bridge.handleGeckoMessage(JSON.stringify({
+          gecko: {
+            type: "Update:Check",
+          }
+        }));
       }
 
       let updateLink = document.getElementById("updateLink");
       let checkingSpan = document.getElementById("update-message-checking");
       let noneSpan = document.getElementById("update-message-none");
       let foundSpan = document.getElementById("update-message-found");
 
       function showCheckingMessage() {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6559,16 +6559,20 @@ var Telemetry = {
 };
 
 let Reader = {
   // Version of the cache database schema
   DB_VERSION: 1,
 
   DEBUG: 1,
 
+  // Don't try to parse the page if it has too many elements (for memory and
+  // performance reasons)
+  MAX_ELEMS_TO_PARSE: 3000,
+
   init: function Reader_init() {
     this.log("Init()");
     this._requests = {};
 
     Services.obs.addObserver(this, "Reader:Add", false);
     Services.obs.addObserver(this, "Reader:Remove", false);
   },
 
@@ -6796,16 +6800,23 @@ let Reader = {
   },
 
   log: function(msg) {
     if (this.DEBUG)
       dump("Reader: " + msg);
   },
 
   _readerParse: function Reader_readerParse(uri, doc, callback) {
+    let numTags = doc.getElementsByTagName("*").length;
+    if (numTags > this.MAX_ELEMS_TO_PARSE) {
+      this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
+      callback(null);
+      return;
+    }
+
     let worker = new ChromeWorker("readerWorker.js");
     worker.onmessage = function (evt) {
       let article = evt.data;
 
       // Append URL to the article data. specIgnoringRef will ignore any hash
       // in the URL.
       if (article)
         article.url = uri.specIgnoringRef;
--- a/mobile/android/components/Makefile.in
+++ b/mobile/android/components/Makefile.in
@@ -38,13 +38,9 @@ EXTRA_COMPONENTS = \
         LoginManagerPrompter.js \
         BlocklistPrompt.js \
         $(NULL)
 
 ifdef MOZ_SAFE_BROWSING
 EXTRA_COMPONENTS += SafeBrowsing.js
 endif
 
-ifdef MOZ_UPDATER
-EXTRA_COMPONENTS += UpdatePrompt.js
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -91,14 +91,8 @@ contract @mozilla.org/addons/blocklist-p
 
 #ifdef MOZ_SAFE_BROWSING
 # SafeBrowsing.js
 component {aadaed90-6c03-42d0-924a-fc61198ff283} SafeBrowsing.js
 contract @mozilla.org/safebrowsing/application;1 {aadaed90-6c03-42d0-924a-fc61198ff283}
 category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;1
 #endif
 
-#ifdef MOZ_UPDATER
-# UpdatePrompt.js
-component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
-contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
-#endif
-
deleted file mode 100644
--- a/mobile/android/components/UpdatePrompt.js
+++ /dev/null
@@ -1,316 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-const UPDATE_NOTIFICATION_NAME = "update-app";
-const UPDATE_NOTIFICATION_ICON = "drawable://alert_download";
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-
-XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
-  return Services.strings.createBundle("chrome://mozapps/locale/update/updates.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function aus_gBrandBundle() {
-  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function aus_gBrowserBundle() {
-  return Services.strings.createBundle("chrome://browser/locale/browser.properties");
-});
-
-XPCOMUtils.defineLazyGetter(this, "AddonManager", function() {
-  Cu.import("resource://gre/modules/AddonManager.jsm");
-  return AddonManager;
-});
-
-XPCOMUtils.defineLazyGetter(this, "LocaleRepository", function() {
-  Cu.import("resource://gre/modules/LocaleRepository.jsm");
-  return LocaleRepository;
-});
-
-function getPref(func, preference, defaultValue) {
-  try {
-    return Services.prefs[func](preference);
-  } catch (e) {}
-  return defaultValue;
-}
-
-function sendMessageToJava(aMsg) {
-  let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(aMsg));
-  return JSON.parse(data);
-}
-
-// -----------------------------------------------------------------------
-// Update Prompt
-// -----------------------------------------------------------------------
-
-function UpdatePrompt() { }
-
-UpdatePrompt.prototype = {
-  classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, Ci.nsIRequestObserver, Ci.nsIProgressEventSink]),
-
-  get _enabled() {
-    return !getPref("getBoolPref", "app.update.silent", false);
-  },
-
-  _showNotification: function UP__showNotif(aUpdate, aTitle, aText, aImageUrl, aMode) {
-    let observer = {
-      updatePrompt: this,
-      observe: function (aSubject, aTopic, aData) {
-        switch (aTopic) {
-          case "alertclickcallback":
-            this.updatePrompt._handleUpdate(aUpdate, aMode);
-            break;
-        }
-      }
-    };
-
-    let notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    notifier.showAlertNotification(aImageUrl, aTitle, aText, true, "", observer, UPDATE_NOTIFICATION_NAME);
-  },
-
-  _handleUpdate: function UP__handleUpdate(aUpdate, aMode) {
-    if (aMode == "available") {
-      let window = Services.wm.getMostRecentWindow("navigator:browser");
-      let title = gUpdateBundle.GetStringFromName("updatesfound_" + aUpdate.type + ".title");
-      let brandName = gBrandBundle.GetStringFromName("brandShortName");
-
-      // Unconditionally use the "major" type here as for now it is always a new version
-      // without additional description required for a minor update message
-      let message = gUpdateBundle.formatStringFromName("intro_major", [brandName, aUpdate.displayVersion], 2);
-      let button0 = gUpdateBundle.GetStringFromName("okButton");
-      let button1 = gUpdateBundle.GetStringFromName("askLaterButton");
-      let prompt = Services.prompt;
-      let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_IS_STRING;
-
-      let download = (prompt.confirmEx(window, title, message, flags, button0, button1, null, null, {value: false}) == 0);
-      if (download) {
-        // Start downloading the update in the background
-        let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-        if (aus.downloadUpdate(aUpdate, true) != "failed") {
-          let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [aUpdate.name], 1);
-          this._showNotification(aUpdate, title, "", UPDATE_NOTIFICATION_ICON, "download");
-
-          // Add this UI as a listener for active downloads
-          aus.addDownloadListener(this);
-        }
-      }
-    } else if(aMode == "downloaded") {
-      // Notify all windows that an application quit has been requested
-      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
-      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
-      // If nothing aborted, restart the app
-      if (cancelQuit.data == false) {
-        sendMessageToJava({
-          gecko: {
-            type: "Update:Restart"
-          }
-        });
-      }
-    }
-  },
-
-  _updateDownloadProgress: function UP__updateDownloadProgress(aProgress, aTotal) {
-    let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    if (progressListener)
-      progressListener.onProgress(UPDATE_NOTIFICATION_NAME, aProgress, aTotal);
-  },
-
-  // -------------------------
-  // nsIUpdatePrompt interface
-  // -------------------------
-
-  // Right now this is used only to check for updates in progress
-  checkForUpdates: function UP_checkForUpdates() {
-    if (!this._enabled)
-      return;
-
-    let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-    if (!aus.isDownloading)
-      return;
-
-    let updateManager = Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager);
-
-    let updateName = updateManager.activeUpdate ? updateManager.activeUpdate.name : gBrandBundle.GetStringFromName("brandShortName");
-    let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [updateName], 1);
-
-    this._showNotification(updateManager.activeUpdate, title, "", UPDATE_NOTIFICATION_ICON, "downloading");
-
-    aus.removeDownloadListener(this); // just in case it's already added
-    aus.addDownloadListener(this);
-  },
-
-  showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
-    if (!this._enabled)
-      return;
-
-    const PREF_APP_UPDATE_SKIPNOTIFICATION = "app.update.skipNotification";
-
-    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SKIPNOTIFICATION) &&
-        Services.prefs.getBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION)) {
-      Services.prefs.setBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION, false);
-
-      // Notification was already displayed and clicked, so jump to the next step:
-      // ask the user about downloading update
-      this._handleUpdate(aUpdate, "available");
-      return;
-    }
-
-    let stringsPrefix = "updateAvailable_" + aUpdate.type + ".";
-    let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
-    let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "available");
-  },
-
-  showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
-    if (!this._enabled)
-      return;
-
-    // uninstall all installed locales
-    AddonManager.getAddonsByTypes(["locale"], (function (aAddons) {
-      if (aAddons.length > 0) {
-        let listener = this.getAddonListener(aUpdate, this);
-        AddonManager.addAddonListener(listener);  
-        aAddons.forEach(function(aAddon) {
-          listener._uninstalling.push(aAddon.id);
-          aAddon.uninstall();
-        }, this);
-      } else {
-        this._showDownloadedNotification(aUpdate);
-      }
-    }).bind(this));
-  },
-
-  _showDownloadedNotification: function UP_showDlNotification(aUpdate) {
-    let stringsPrefix = "updateDownloaded_" + aUpdate.type + ".";
-    let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
-    let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "downloaded");
-  },
-
-  _uninstalling: [],
-  _installing: [],
-  _currentUpdate: null,
-
-  _reinstallLocales: function UP_reinstallLocales(aUpdate, aListener, aPending) {
-    LocaleRepository.getLocales((function(aLocales) {
-      aLocales.forEach(function(aLocale, aIndex, aArray) {
-        let index = aPending.indexOf(aLocale.addon.id);
-        if (index > -1) {
-          aListener._installing.push(aLocale.addon.id);
-          aLocale.addon.install.install();
-        }
-      }, this);
-      // store the buildid of these locales so that we can disable locales when the
-      // user updates through a non-updater channel
-      Services.prefs.setCharPref("extensions.compatability.locales.buildid", aUpdate.buildID);
-    }).bind(this), { buildID: aUpdate.buildID });
-  },
-
-  showUpdateInstalled: function UP_showUpdateInstalled() {
-    if (!this._enabled || !getPref("getBoolPref", "app.update.showInstalledUI", false))
-      return;
-
-    let title = gBrandBundle.GetStringFromName("brandShortName");
-    let text = gUpdateBundle.GetStringFromName("installSuccess");
-    let imageUrl = "";
-    this._showNotification(aUpdate, title, text, imageUrl, "installed");
-  },
-
-  showUpdateError: function UP_showUpdateError(aUpdate) {
-    if (!this._enabled)
-      return;
-
-    if (aUpdate.state == "failed") {
-      var title = gBrandBundle.GetStringFromName("brandShortName");
-      let text = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
-      let imageUrl = "";
-      this._showNotification(aUpdate, title, text, imageUrl, "error");
-    }
-  },
-
-  showUpdateHistory: function UP_showUpdateHistory(aParent) {
-    // NOT IMPL
-  },
-  
-  // ----------------------------
-  // nsIRequestObserver interface
-  // ----------------------------
-  
-  // When the data transfer begins
-  onStartRequest: function(request, context) {
-    // NOT IMPL
-  },
-
-  // When the data transfer ends
-  onStopRequest: function(request, context, status) {
-    let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    if (progressListener)
-      progressListener.onCancel(UPDATE_NOTIFICATION_NAME);
-
-
-    let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
-    aus.removeDownloadListener(this);
-  },
-
-  // ------------------------------
-  // nsIProgressEventSink interface
-  // ------------------------------
-  
-  // When new data has been downloaded
-  onProgress: function(request, context, progress, maxProgress) {
-    this._updateDownloadProgress(progress, maxProgress);
-  },
-
-  // When we have new status text
-  onStatus: function(request, context, status, statusText) {
-    // NOT IMPL
-  },
-
-  // -------------------------------
-  // AddonListener
-  // -------------------------------
-  getAddonListener: function(aUpdate, aUpdatePrompt) {
-    return {
-      _installing: [],
-      _uninstalling: [],
-      onInstalling: function(aAddon, aNeedsRestart) {
-        let index = this._installing.indexOf(aAddon.id);
-        if (index > -1)
-          this._installing.splice(index, 1);
-    
-        if (this._installing.length == 0) {
-          aUpdatePrompt._showDownloadedNotification(aUpdate);
-          AddonManager.removeAddonListener(this);
-        }
-      },
-    
-      onUninstalling: function(aAddon, aNeedsRestart) {
-        let pending = [];
-        let index = this._uninstalling.indexOf(aAddon.id);
-        if (index > -1) {
-          pending.push(aAddon.id);
-          this._uninstalling.splice(index, 1);
-        }
-        if (this._uninstalling.length == 0)
-          aUpdatePrompt._reinstallLocales(aUpdate, this, pending);
-      }
-    }
-  }
-
-};
-
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -511,14 +511,11 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/MobileComponents.manifest
 @BINPATH@/components/MobileComponents.xpt
 @BINPATH@/components/PromptService.js
 @BINPATH@/components/SessionStore.js
 @BINPATH@/components/Sidebar.js
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/SafeBrowsing.js
 #endif
-#ifdef MOZ_UPDATER
-@BINPATH@/components/UpdatePrompt.js
-#endif
 @BINPATH@/components/XPIDialogService.js
 @BINPATH@/components/browsercomps.xpt
 @BINPATH@/extensions/feedback@mobile.mozilla.org.xpi
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -68,19 +68,17 @@ nsHTTPDownloadEvent::Run()
 
   nsCOMPtr<nsIIOService> ios = do_GetIOService();
   NS_ENSURE_STATE(ios);
 
   nsCOMPtr<nsIChannel> chan;
   ios->NewChannel(mRequestSession->mURL, nullptr, nullptr, getter_AddRefs(chan));
   NS_ENSURE_STATE(chan);
 
-  // Disabled because it breaks authentication with a proxy, when such proxy
-  // had been setup, and brings blue UI for EV certs.
-  // chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
+  chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
 
   // Create a loadgroup for this new channel.  This way if the channel
   // is redirected, we'll have a way to cancel the resulting channel.
   nsCOMPtr<nsILoadGroup> lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   chan->SetLoadGroup(lg);
 
   if (mRequestSession->mHasPostData)
   {
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -252,20 +252,23 @@ function synthesizeTouchAtCenter(aTarget
 /**
  * Synthesize a wheel event on a target. The actual client point is determined
  * by taking the aTarget's client box and offseting it by aOffsetX and
  * aOffsetY.
  *
  * aEvent is an object which may contain the properties:
  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ,
  *   deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice,
- *   isCustomizedByPrefs
+ *   isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY
  *
  * deltaMode must be defined, others are ok even if undefined.
  *
+ * expectedOverflowDeltaX and expectedOverflowDeltaY take integer value.  The
+ * value is just checked as 0 or positive or negative.
+ *
  * aWindow is optional, and defaults to the current window object.
  */
 function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
 {
   var utils = _getDOMWindowUtils(aWindow);
   if (!utils) {
     return;
   }
@@ -277,16 +280,34 @@ function synthesizeWheel(aTarget, aOffse
     options |= utils.WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE;
   }
   if (aEvent.isMomentum) {
     options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
   }
   if (aEvent.isCustomizedByPrefs) {
     options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS;
   }
+  if (typeof aEvent.expectedOverflowDeltaX !== "undefined") {
+    if (aEvent.expectedOverflowDeltaX === 0) {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO;
+    } else if (aEvent.expectedOverflowDeltaX > 0) {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE;
+    } else {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE;
+    }
+  }
+  if (typeof aEvent.expectedOverflowDeltaY !== "undefined") {
+    if (aEvent.expectedOverflowDeltaY === 0) {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO;
+    } else if (aEvent.expectedOverflowDeltaY > 0) {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE;
+    } else {
+      options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE;
+    }
+  }
   var isPixelOnlyDevice =
     aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL;
   var lineOrPageDeltaX =
     aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX :
                   aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) :
                                       Math.ceil(aEvent.deltaX);
   var lineOrPageDeltaY =
     aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY :
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -404,16 +404,22 @@ OS_LIBS += \
   -lutils \
   -lcutils \
   -lsysutils \
   -lcamera_client \
   -lbinder \
   -lsensorservice \
   -ldbus \
   $(NULL)
+
+ifdef MOZ_CUBEB
+OS_LIBS += \
+  -lOpenSLES \
+  $(NULL)
+endif
 endif
 
 EXTRA_DEPS += \
   $(topsrcdir)/intl/unicharutil/util/objs.mk \
   $(topsrcdir)/rdf/util/src/objs.mk \
   $(NULL)
 
 CPPSRCS += \
--- a/tools/profiler/shared-libraries-linux.cc
+++ b/tools/profiler/shared-libraries-linux.cc
@@ -24,30 +24,30 @@ static ssize_t getline(char **lineptr, s
  }
  ret = fgets(*lineptr, 4096, stream);
  if (!ret)
    return 0;
  return strlen(*lineptr);
 }
 #endif
 
-#ifndef MOZ_OLD_LINKER
+#if !defined(MOZ_OLD_LINKER) && !defined(MOZ_WIDGET_GONK)
 // TODO fix me with proper include
 #include "nsDebug.h"
 #ifdef ANDROID
 #include "ElfLoader.h" // dl_phdr_info
 #else
 #include <link.h> // dl_phdr_info
 #endif
 #include <features.h>
 #include <dlfcn.h>
 #include <sys/types.h>
 
 #ifdef ANDROID
-__attribute__((weak))
+extern "C" __attribute__((weak))
 int dl_iterate_phdr(
           int (*callback) (struct dl_phdr_info *info,
                            size_t size, void *data),
           void *data);
 #endif
 
 int dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
 {
@@ -76,17 +76,17 @@ int dl_iterate_callback(struct dl_phdr_i
   return 0;
 }
 #endif
 
 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
 {
   SharedLibraryInfo info;
 
-#ifndef MOZ_OLD_LINKER
+#if !defined(MOZ_OLD_LINKER) && !defined(MOZ_WIDGET_GONK)
   dl_iterate_phdr(dl_iterate_callback, &info);
 #ifndef ANDROID
   return info;
 #endif
 #endif
 
   pid_t pid = getpid();
   char path[PATH_MAX];
@@ -109,17 +109,17 @@ SharedLibraryInfo SharedLibraryInfo::Get
     if (!strchr(perm, 'x')) {
       // Ignore non executable entries
       continue;
     }
     if (ret != 5 && ret != 4) {
       LOG("Get maps line failed");
       continue;
     }
-#if defined(ANDROID) && !defined(MOZ_OLD_LINKER)
+#if defined(ANDROID) && !defined(MOZ_OLD_LINKER) && !defined(MOZ_WIDGET_GONK)
     // Use proc/pid/maps to get the dalvik-jit section since it has
     // no associated phdrs
     if (strcmp(name, "/dev/ashmem/dalvik-jit-code-cache") != 0)
       continue;
 #endif
     SharedLibrary shlib(start, end, offset, name);
     info.AddSharedLibrary(shlib);
     if (count > 10000) {