Merge m-c to b-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 26 Oct 2013 18:51:32 -0700
changeset 166264 d5219c34bfb14328b26ec11cdfd77003835ab35b
parent 166263 2db069bf99081e67d2678001a7486e36fed60b35 (current diff)
parent 166178 a80dce1126db109fdbf19a3bde050c2bd580c834 (diff)
child 166265 795b14516359f38351feb052fd475d3f6cec2811
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b-i CLOSED TREE
toolkit/mozapps/update/test/Makefile.in
toolkit/mozapps/update/test/TestAUSHelper.cpp
toolkit/mozapps/update/test/TestAUSReadStrings.cpp
toolkit/mozapps/update/test/TestAUSReadStrings1.ini
toolkit/mozapps/update/test/TestAUSReadStrings2.ini
toolkit/mozapps/update/test/TestAUSReadStrings3.ini
toolkit/mozapps/update/test/chrome/Makefile.in
toolkit/mozapps/update/test/chrome/chrome.ini
toolkit/mozapps/update/test/chrome/moz.build
toolkit/mozapps/update/test/chrome/test_0011_check_basic.xul
toolkit/mozapps/update/test/chrome/test_0012_check_basic_license.xul
toolkit/mozapps/update/test/chrome/test_0013_check_incompat_basic.xul
toolkit/mozapps/update/test/chrome/test_0014_check_incompat_basic_license.xul
toolkit/mozapps/update/test/chrome/test_0015_check_incompat_basic_addons.xul
toolkit/mozapps/update/test/chrome/test_0016_check_incompat_basic_license_addons.xul
toolkit/mozapps/update/test/chrome/test_0017_check_staging_basic.xul
toolkit/mozapps/update/test/chrome/test_0021_check_billboard.xul
toolkit/mozapps/update/test/chrome/test_0022_check_billboard_license.xul
toolkit/mozapps/update/test/chrome/test_0023_check_incompat_billboard.xul
toolkit/mozapps/update/test/chrome/test_0024_check_incompat_billboard_license.xul
toolkit/mozapps/update/test/chrome/test_0025_check_incompat_billboard_addons.xul
toolkit/mozapps/update/test/chrome/test_0026_check_incompat_billboard_license_addons.xul
toolkit/mozapps/update/test/chrome/test_0031_available_basic.xul
toolkit/mozapps/update/test/chrome/test_0032_available_basic_license.xul
toolkit/mozapps/update/test/chrome/test_0033_available_incompat_basic.xul
toolkit/mozapps/update/test/chrome/test_0034_available_incompat_basic_license.xul
toolkit/mozapps/update/test/chrome/test_0035_available_incompat_basic_addons.xul
toolkit/mozapps/update/test/chrome/test_0036_available_incompat_basic_license_addons.xul
toolkit/mozapps/update/test/chrome/test_0041_available_billboard.xul
toolkit/mozapps/update/test/chrome/test_0042_available_billboard_license.xul
toolkit/mozapps/update/test/chrome/test_0043_available_incompat_billboard.xul
toolkit/mozapps/update/test/chrome/test_0044_available_incompat_billboard_license.xul
toolkit/mozapps/update/test/chrome/test_0045_available_incompat_billboard_addons.xul
toolkit/mozapps/update/test/chrome/test_0046_available_incompat_billboard_license_addons.xul
toolkit/mozapps/update/test/chrome/test_0051_check_error_xml_malformed.xul
toolkit/mozapps/update/test/chrome/test_0052_check_no_updates.xul
toolkit/mozapps/update/test/chrome/test_0053_check_billboard_license_noAttr.xul
toolkit/mozapps/update/test/chrome/test_0054_check_billboard_license_404.xul
toolkit/mozapps/update/test/chrome/test_0061_check_verifyFailPartial_noComplete.xul
toolkit/mozapps/update/test/chrome/test_0062_check_verifyFailComplete_noPartial.xul
toolkit/mozapps/update/test/chrome/test_0063_check_verifyFailPartialComplete.xul
toolkit/mozapps/update/test/chrome/test_0064_check_verifyFailPartial_successComplete.xul
toolkit/mozapps/update/test/chrome/test_0071_notify_verifyFailPartial_noComplete.xul
toolkit/mozapps/update/test/chrome/test_0072_notify_verifyFailComplete_noPartial.xul
toolkit/mozapps/update/test/chrome/test_0073_notify_verifyFailPartialComplete.xul
toolkit/mozapps/update/test/chrome/test_0074_notify_verifyFailPartial_successComplete.xul
toolkit/mozapps/update/test/chrome/test_0081_error_patchApplyFailure_partial_only.xul
toolkit/mozapps/update/test/chrome/test_0082_error_patchApplyFailure_complete_only.xul
toolkit/mozapps/update/test/chrome/test_0083_error_patchApplyFailure_partial_complete.xul
toolkit/mozapps/update/test/chrome/test_0084_error_patchApplyFailure_verify_failed.xul
toolkit/mozapps/update/test/chrome/test_0091_installed.xul
toolkit/mozapps/update/test/chrome/test_0092_finishedBackground.xul
toolkit/mozapps/update/test/chrome/test_0093_restartNotification.xul
toolkit/mozapps/update/test/chrome/test_0094_restartNotification_remote.xul
toolkit/mozapps/update/test/chrome/test_0095_restartNotification_remoteInvalidNumber.xul
toolkit/mozapps/update/test/chrome/test_0096_restartNotification_stagedBackground.xul
toolkit/mozapps/update/test/chrome/test_0097_restartNotification_stagedServiceBackground.xul
toolkit/mozapps/update/test/chrome/test_0101_background_restartNotification.xul
toolkit/mozapps/update/test/chrome/test_0102_background_restartNotification_staging.xul
toolkit/mozapps/update/test/chrome/test_0103_background_restartNotification_stagingService.xul
toolkit/mozapps/update/test/chrome/test_0111_neverButton_basic.xul
toolkit/mozapps/update/test/chrome/test_0112_neverButton_billboard.xul
toolkit/mozapps/update/test/chrome/test_0113_showNeverForVersionRemovedWithPref.xul
toolkit/mozapps/update/test/chrome/test_0121_check_requireBuiltinCert.xul
toolkit/mozapps/update/test/chrome/test_0122_check_allowNonBuiltinCert_validCertAttrs.xul
toolkit/mozapps/update/test/chrome/test_0123_check_allowNonBuiltinCert_noCertAttrsCheck.xul
toolkit/mozapps/update/test/chrome/test_0131_check_invalidCertAttrs_noUpdate.xul
toolkit/mozapps/update/test/chrome/test_0132_check_invalidCertAttrs_hasUpdate.xul
toolkit/mozapps/update/test/chrome/test_0141_notify_invalidCertAttrs_noUpdate.xul
toolkit/mozapps/update/test/chrome/test_0142_notify_invalidCertAttrs_hasUpdate.xul
toolkit/mozapps/update/test/chrome/test_0151_notify_backgroundCheckError.xul
toolkit/mozapps/update/test/chrome/test_0161_check_unsupported.xul
toolkit/mozapps/update/test/chrome/test_0162_notify_unsupported.xul
toolkit/mozapps/update/test/chrome/test_0900_deprecatedUpdateFormat_minor.xul
toolkit/mozapps/update/test/chrome/test_0901_deprecatedUpdateFormat_major.xul
toolkit/mozapps/update/test/chrome/test_9999_cleanup.xul
toolkit/mozapps/update/test/chrome/update.sjs
toolkit/mozapps/update/test/chrome/utils.js
toolkit/mozapps/update/test/marionette/data/bad.xml
toolkit/mozapps/update/test/marionette/data/err.cgi
toolkit/mozapps/update/test/marionette/update-smoketests.ini
toolkit/mozapps/update/test/marionette/update-tests.ini
toolkit/mozapps/update/test/marionette/update_smoketest_ota_same_version.js
toolkit/mozapps/update/test/marionette/update_smoketest_ota_same_version.py
toolkit/mozapps/update/test/marionette/update_smoketest_ota_simple.js
toolkit/mozapps/update/test/marionette/update_smoketest_ota_simple.py
toolkit/mozapps/update/test/marionette/update_test_ota_simple.js
toolkit/mozapps/update/test/marionette/update_test_ota_simple.py
toolkit/mozapps/update/test/marionette/update_test_status.js
toolkit/mozapps/update/test/marionette/update_test_status.py
toolkit/mozapps/update/test/moz.build
toolkit/mozapps/update/test/shared.js
toolkit/mozapps/update/test/sharedUpdateXML.js
toolkit/mozapps/update/test/unit/data/complete.mar
toolkit/mozapps/update/test/unit/data/complete.png
toolkit/mozapps/update/test/unit/data/complete_cc_log_switch_success
toolkit/mozapps/update/test/unit/data/complete_log_success
toolkit/mozapps/update/test/unit/data/complete_log_switch_success
toolkit/mozapps/update/test/unit/data/complete_precomplete
toolkit/mozapps/update/test/unit/data/complete_removed-files
toolkit/mozapps/update/test/unit/data/complete_update_manifest
toolkit/mozapps/update/test/unit/data/complete_win.mar
toolkit/mozapps/update/test/unit/data/old_version_mar.mar
toolkit/mozapps/update/test/unit/data/partial.mar
toolkit/mozapps/update/test/unit/data/partial.png
toolkit/mozapps/update/test/unit/data/partial_in_use_win_after.exe
toolkit/mozapps/update/test/unit/data/partial_in_use_win_before.exe
toolkit/mozapps/update/test/unit/data/partial_log_failure
toolkit/mozapps/update/test/unit/data/partial_log_success
toolkit/mozapps/update/test/unit/data/partial_log_switch_success
toolkit/mozapps/update/test/unit/data/partial_precomplete
toolkit/mozapps/update/test/unit/data/partial_removed-files
toolkit/mozapps/update/test/unit/data/partial_update_manifest
toolkit/mozapps/update/test/unit/data/partial_win.mar
toolkit/mozapps/update/test/unit/data/simple.mar
toolkit/mozapps/update/test/unit/data/simple_no_pib.mar
toolkit/mozapps/update/test/unit/data/wrong_product_channel_mar.mar
toolkit/mozapps/update/test/unit/head_update.js.in
toolkit/mozapps/update/test/unit/test_0010_general.js
toolkit/mozapps/update/test/unit/test_0020_general.js
toolkit/mozapps/update/test/unit/test_0030_general.js
toolkit/mozapps/update/test/unit/test_0040_general.js
toolkit/mozapps/update/test/unit/test_0050_general.js
toolkit/mozapps/update/test/unit/test_0060_manager.js
toolkit/mozapps/update/test/unit/test_0061_manager.js
toolkit/mozapps/update/test/unit/test_0062_manager.js
toolkit/mozapps/update/test/unit/test_0063_manager.js
toolkit/mozapps/update/test/unit/test_0064_manager.js
toolkit/mozapps/update/test/unit/test_0070_update_dir_cleanup.js
toolkit/mozapps/update/test/unit/test_0071_update_dir_cleanup.js
toolkit/mozapps/update/test/unit/test_0072_update_dir_cleanup.js
toolkit/mozapps/update/test/unit/test_0073_update_dir_cleanup.js
toolkit/mozapps/update/test/unit/test_0080_prompt_silent.js
toolkit/mozapps/update/test/unit/test_0081_prompt_uiAlreadyOpen.js
toolkit/mozapps/update/test/unit/test_0082_prompt_unsupportAlreadyNotified.js
toolkit/mozapps/update/test/unit/test_0110_general.js
toolkit/mozapps/update/test/unit/test_0111_general.js
toolkit/mozapps/update/test/unit/test_0112_general.js
toolkit/mozapps/update/test/unit/test_0113_general.js
toolkit/mozapps/update/test/unit/test_0113_versionDowngradeCheck.js
toolkit/mozapps/update/test/unit/test_0114_general.js
toolkit/mozapps/update/test/unit/test_0114_productChannelCheck.js
toolkit/mozapps/update/test/unit/test_0115_general.js
toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0152_appBinReplaced_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0153_appBinPatched_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0160_appInUse_complete.js
toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_unix_complete.js
toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0162_appInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0174_fileLocked_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0175_fileLocked_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0180_fileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0181_fileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0182_rmrfdirFileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0183_rmrfdirFileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0188_fileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0189_fileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0190_rmrfdirFileInUse_xp_win_complete.js
toolkit/mozapps/update/test/unit/test_0191_rmrfdirFileInUse_xp_win_partial.js
toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js
toolkit/mozapps/update/test/unit/test_0202_app_launch_apply_update_dirlocked.js
toolkit/mozapps/update/test/unit/test_0203_app_launch_apply_update.js
toolkit/mozapps/update/test/unit/test_0300_update_root_dir_migration.js
toolkit/mozapps/update/test/unit/test_bug595059.js
toolkit/mozapps/update/test/unit/test_bug794211.js
toolkit/mozapps/update/test/unit/test_bug833708.js
toolkit/mozapps/update/test/unit/xpcshell.ini
toolkit/mozapps/update/test/unit/xpcshell_updater.ini
toolkit/mozapps/update/test_svc/Makefile.in
toolkit/mozapps/update/test_svc/moz.build
toolkit/mozapps/update/test_svc/unit/test_0000_bootstrap_svc.js
toolkit/mozapps/update/test_svc/unit/test_0110_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0111_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0112_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0113_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0114_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0115_general_svc.js
toolkit/mozapps/update/test_svc/unit/test_0150_appBinReplaced_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0151_appBinPatched_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0152_appBinReplaced_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0153_appBinPatched_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0160_appInUse_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0162_appInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0170_fileLocked_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0171_fileLocked_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0174_fileLocked_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0175_fileLocked_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0180_fileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0181_fileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0182_rmrfdirFileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0183_rmrfdirFileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0188_fileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0189_fileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0190_rmrfdirFileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test_svc/unit/test_0191_rmrfdirFileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test_svc/unit/test_0200_app_launch_apply_update_svc.js
toolkit/mozapps/update/test_svc/unit/test_0201_app_launch_apply_update_svc.js
toolkit/mozapps/update/test_svc/unit/test_0202_app_launch_apply_update_dirlocked_svc.js
toolkit/mozapps/update/test_svc/unit/test_0203_app_launch_apply_update_svc.js
toolkit/mozapps/update/test_svc/unit/xpcshell.ini
toolkit/mozapps/update/test_timermanager/moz.build
toolkit/mozapps/update/test_timermanager/unit/test_0010_timermanager.js
toolkit/mozapps/update/test_timermanager/unit/xpcshell.ini
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -11,16 +11,20 @@
 #include "xpcAccEvents.h"
 #include "States.h"
 
 #include "nsEventStateManager.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
+static_assert(static_cast<bool>(eNoUserInput) == false &&
+              static_cast<bool>(eFromUserInput) == true,
+              "EIsFromUserInput cannot be casted to bool");
+
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent constructors
 
 AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
@@ -113,16 +117,28 @@ AccHideEvent::
 AccShowEvent::
   AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) :
   AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
 {
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// AccTextSelChangeEvent
+////////////////////////////////////////////////////////////////////////////////
+
+AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
+                                             nsISelection* aSelection) :
+  AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
+           eAutoDetect, eCoalesceTextSelChange),
+  mSel(aSelection) {}
+
+AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
+
+////////////////////////////////////////////////////////////////////////////////
 // AccSelChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 AccSelChangeEvent::
   AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
                     SelChangeType aSelChangeType) :
     AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
     mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -5,16 +5,18 @@
 
 #ifndef _AccEvent_H_
 #define _AccEvent_H_
 
 #include "nsIAccessibleEvent.h"
 
 #include "mozilla/a11y/Accessible.h"
 
+class nsISelection;
+
 namespace mozilla {
 namespace a11y {
 
 class DocAccessible;
 
 // Constants used to point whether the event is from user input.
 enum EIsFromUserInput
 {
@@ -54,16 +56,19 @@ public:
     eCoalesceOfSameType,
 
     // eCoalesceSelectionChange: coalescence of selection change events.
     eCoalesceSelectionChange,
 
     // eCoalesceStateChange: coalesce state change events.
     eCoalesceStateChange,
 
+    // eCoalesceTextSelChange: coalescence of text selection change events.
+    eCoalesceTextSelChange,
+
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
     eRemoveDupes,
 
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
     eDoNotEmit
   };
 
@@ -72,32 +77,35 @@ public:
            EIsFromUserInput aIsFromUserInput = eAutoDetect,
            EEventRule aEventRule = eRemoveDupes);
   virtual ~AccEvent() {}
 
   // AccEvent
   uint32_t GetEventType() const { return mEventType; }
   EEventRule GetEventRule() const { return mEventRule; }
   bool IsFromUserInput() const { return mIsFromUserInput; }
+  EIsFromUserInput FromUserInput() const
+    { return static_cast<EIsFromUserInput>(mIsFromUserInput); }
 
   Accessible* GetAccessible() const { return mAccessible; }
   DocAccessible* GetDocAccessible() const { return mAccessible->Document(); }
 
   /**
    * Down casting.
    */
   enum EventGroup {
     eGenericEvent,
     eStateChangeEvent,
     eTextChangeEvent,
     eMutationEvent,
     eReorderEvent,
     eHideEvent,
     eShowEvent,
     eCaretMoveEvent,
+    eTextSelChangeEvent,
     eSelectionChangeEvent,
     eTableChangeEvent,
     eVirtualCursorChangeEvent
   };
 
   static const EventGroup kEventGroup = eGenericEvent;
   virtual unsigned int GetEventGroups() const
   {
@@ -325,35 +333,59 @@ protected:
 
 
 /**
  * Accessible caret move event.
  */
 class AccCaretMoveEvent: public AccEvent
 {
 public:
-  AccCaretMoveEvent(Accessible* aAccessible) :
-    AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
-    mCaretOffset(-1) { }
+  AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset,
+                    EIsFromUserInput aIsFromUserInput = eAutoDetect) :
+    AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
+             aIsFromUserInput),
+    mCaretOffset(aCaretOffset) { }
   virtual ~AccCaretMoveEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eCaretMoveEvent;
   virtual unsigned int GetEventGroups() const
   {
     return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent);
   }
 
   // AccCaretMoveEvent
   int32_t GetCaretOffset() const { return mCaretOffset; }
 
 private:
   int32_t mCaretOffset;
+};
+
+
+/**
+ * Accessible text selection change event.
+ */
+class AccTextSelChangeEvent : public AccEvent
+{
+public:
+  AccTextSelChangeEvent(HyperTextAccessible* aTarget, nsISelection* aSelection);
+  virtual ~AccTextSelChangeEvent();
+
+  // AccEvent
+  static const EventGroup kEventGroup = eTextSelChangeEvent;
+  virtual unsigned int GetEventGroups() const
+  {
+    return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent);
+  }
+
+private:
+  nsCOMPtr<nsISelection> mSel;
 
   friend class EventQueue;
+  friend class SelectionManager;
 };
 
 
 /**
  * Accessible widget selection change event.
  */
 class AccSelChangeEvent : public AccEvent
 {
--- a/accessible/src/base/EventQueue.cpp
+++ b/accessible/src/base/EventQueue.cpp
@@ -147,16 +147,36 @@ EventQueue::CoalesceEvents()
             if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
               tailEvent->mEventRule = AccEvent::eDoNotEmit;
           }
         }
       }
       break; // eCoalesceStateChange
     }
 
+    case AccEvent::eCoalesceTextSelChange:
+    {
+      // Coalesce older event by newer event for the same selection or target.
+      // Events for same selection may have different targets and vice versa one
+      // target may be pointed by different selections (for latter see
+      // bug 927159).
+      for (uint32_t index = tail - 1; index < tail; index--) {
+        AccEvent* thisEvent = mEvents[index];
+        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
+            thisEvent->mEventType == tailEvent->mEventType) {
+          AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
+          AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
+          if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
+              thisEvent->mAccessible == tailEvent->mAccessible)
+            thisEvent->mEventRule = AccEvent::eDoNotEmit;
+        }
+
+      }
+    } break; // eCoalesceTextSelChange
+
     case AccEvent::eRemoveDupes:
     {
       // Check for repeat events, coalesce newly appended event by more older
       // event.
       for (uint32_t index = tail - 1; index < tail; index--) {
         AccEvent* accEvent = mEvents[index];
         if (accEvent->mEventType == tailEvent->mEventType &&
           accEvent->mEventRule == tailEvent->mEventRule &&
@@ -453,31 +473,18 @@ EventQueue::ProcessEventQueue()
 
       // Dispatch the focus event if target is still focused.
       if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
         FocusMgr()->ProcessFocusEvent(event);
         continue;
       }
 
       // Dispatch caret moved and text selection change events.
-      if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
-        AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
-        HyperTextAccessible* hyperText = target->AsHyperText();
-        if (hyperText &&
-            NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {
-
-          nsEventShell::FireEvent(caretMoveEvent);
-
-          // There's a selection so fire selection change as well.
-          int32_t selectionCount;
-          hyperText->GetSelectionCount(&selectionCount);
-          if (selectionCount)
-            nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
-                                    hyperText);
-        }
+      if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
+        SelectionMgr()->ProcessTextSelChangeEvent(event);
         continue;
       }
 
       // Fire selected state change events in support to selection events.
       if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
         nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
                                 true, event->mIsFromUserInput);
 
--- a/accessible/src/base/FocusManager.cpp
+++ b/accessible/src/base/FocusManager.cpp
@@ -260,19 +260,16 @@ FocusManager::ProcessDOMFocus(nsINode* a
 }
 
 void
 FocusManager::ProcessFocusEvent(AccEvent* aEvent)
 {
   NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
                   "Focus event is expected!");
 
-  EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
-    eFromUserInput : eNoUserInput;
-
   // Emit focus event if event target is the active item. Otherwise then check
   // if it's still focused and then update active item and emit focus event.
   Accessible* target = aEvent->GetAccessible();
   if (target != mActiveItem) {
 
     // Check if still focused. Otherwise we can end up with storing the active
     // item for control that isn't focused anymore.
     DocAccessible* document = aEvent->GetDocAccessible();
@@ -294,60 +291,60 @@ FocusManager::ProcessFocusEvent(AccEvent
     Accessible* ARIAMenubar =
       nsAccUtils::GetAncestorWithRole(target, roles::MENUBAR);
 
     if (ARIAMenubar != mActiveARIAMenubar) {
       // Leaving ARIA menu. Fire menu_end event on current menubar.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuEndEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
-                       fromUserInputFlag);
+                       aEvent->FromUserInput());
         nsEventShell::FireEvent(menuEndEvent);
       }
 
       mActiveARIAMenubar = ARIAMenubar;
 
       // Entering ARIA menu. Fire menu_start event.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuStartEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
-                       mActiveARIAMenubar, fromUserInputFlag);
+                       mActiveARIAMenubar, aEvent->FromUserInput());
         nsEventShell::FireEvent(menuStartEvent);
       }
     }
   } else if (mActiveARIAMenubar) {
     // Focus left a menu. Fire menu_end event.
     nsRefPtr<AccEvent> menuEndEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
-                   fromUserInputFlag);
+                   aEvent->FromUserInput());
     nsEventShell::FireEvent(menuEndEvent);
 
     mActiveARIAMenubar = nullptr;
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eFocus))
     logging::FocusNotificationTarget("fire focus event", "Target", target);
 #endif
 
   nsRefPtr<AccEvent> focusEvent =
-    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
+    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
   nsEventShell::FireEvent(focusEvent);
 
   // Fire scrolling_start event when the document receives the focus if it has
   // an anchor jump. If an accessible within the document receive the focus
   // then null out the anchor jump because it no longer applies.
   DocAccessible* targetDocument = target->Document();
   Accessible* anchorJump = targetDocument->AnchorJump();
   if (anchorJump) {
     if (target == targetDocument) {
       // XXX: bug 625699, note in some cases the node could go away before we
       // we receive focus event, for example if the node is removed from DOM.
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
-                              anchorJump, fromUserInputFlag);
+                              anchorJump, aEvent->FromUserInput());
     }
     targetDocument->SetAnchorJump(nullptr);
   }
 }
 
 nsINode*
 FocusManager::FocusedDOMNode() const
 {
--- a/accessible/src/base/SelectionManager.cpp
+++ b/accessible/src/base/SelectionManager.cpp
@@ -4,39 +4,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/a11y/SelectionManager.h"
 
 #include "DocAccessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
-#include "nsIAccessibleEvent.h"
+#include "nsEventShell.h"
 
-#include "nsCaret.h"
 #include "nsIAccessibleTypes.h"
 #include "nsIDOMDocument.h"
-#include "nsIFrame.h"
 #include "nsIPresShell.h"
 #include "nsISelectionPrivate.h"
 #include "mozilla/Selection.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 void
-SelectionManager::Shutdown()
-{
-  ClearControlSelectionListener();
-  mLastTextAccessible = nullptr;
-  mLastUsedSelection = nullptr;
-}
-
-void
 SelectionManager::ClearControlSelectionListener()
 {
   if (!mCurrCtrlFrame)
     return;
 
   const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for the element!");
 
@@ -59,18 +49,16 @@ SelectionManager::ClearControlSelectionL
 void
 SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
 {
   // When focus moves such that the caret is part of a new frame selection
   // this removes the old selection listener and attaches a new one for
   // the current focus.
   ClearControlSelectionListener();
 
-  mLastTextAccessible = nullptr;
-
   mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
   if (!mCurrCtrlFrame)
     return;
 
   const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for focused element!");
   if (!frameSel)
     return;
@@ -114,16 +102,47 @@ SelectionManager::RemoveDocSelectionList
 
   // Remove 'this' registered as selection listener for the spellcheck
   // selection.
   Selection* spellSel =
     frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
   spellSel->RemoveSelectionListener(this);
 }
 
+void
+SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent)
+{
+  AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
+  Selection* sel = static_cast<Selection*>(event->mSel.get());
+
+  // Fire selection change event if it's not pure caret-move selection change.
+  if (sel->GetRangeCount() != 1 || !sel->IsCollapsed())
+    nsEventShell::FireEvent(aEvent);
+
+  // Fire caret move event if there's a caret in the selection.
+  nsINode* caretCntrNode =
+    nsCoreUtils::GetDOMNodeFromDOMPoint(sel->GetFocusNode(),
+                                        sel->GetFocusOffset());
+  if (!caretCntrNode)
+    return;
+
+  HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode);
+  NS_ASSERTION(caretCntr,
+               "No text container for focus while there's one for common ancestor?!");
+  if (!caretCntr)
+    return;
+
+  int32_t caretOffset = -1;
+  if (NS_SUCCEEDED(caretCntr->GetCaretOffset(&caretOffset)) && caretOffset != -1) {
+    nsRefPtr<AccCaretMoveEvent> caretMoveEvent =
+      new AccCaretMoveEvent(caretCntr, caretOffset, aEvent->FromUserInput());
+    nsEventShell::FireEvent(caretMoveEvent);
+  }
+}
+
 NS_IMETHODIMP
 SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
                                          nsISelection* aSelection,
                                          int16_t aReason)
 {
   NS_ENSURE_ARG(aDOMDocument);
 
   nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
@@ -144,133 +163,39 @@ SelectionManager::NotifySelectionChanged
   }
 
   return NS_OK;
 }
 
 void
 SelectionManager::ProcessSelectionChanged(nsISelection* aSelection)
 {
-  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
-
-  int16_t type = 0;
-  privSel->GetType(&type);
-
-  if (type == nsISelectionController::SELECTION_NORMAL)
-    NormalSelectionChanged(aSelection);
-
-  else if (type == nsISelectionController::SELECTION_SPELLCHECK)
-    SpellcheckSelectionChanged(aSelection);
-}
-
-void
-SelectionManager::NormalSelectionChanged(nsISelection* aSelection)
-{
-  mLastUsedSelection = do_GetWeakReference(aSelection);
-
-  int32_t rangeCount = 0;
-  aSelection->GetRangeCount(&rangeCount);
-  if (rangeCount == 0) {
-    mLastTextAccessible = nullptr;
-    return; // No selection
-  }
-
-  HyperTextAccessible* textAcc =
-    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
-  if (!textAcc)
-    return;
-
-  int32_t caretOffset = -1;
-  nsresult rv = textAcc->GetCaretOffset(&caretOffset);
-  if (NS_FAILED(rv))
-    return;
-
-  if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) {
-    int32_t selectionCount = 0;
-    textAcc->GetSelectionCount(&selectionCount);   // Don't swallow similar events when selecting text
-    if (!selectionCount)
-      return;  // Swallow duplicate caret event
+  Selection* selection = static_cast<Selection*>(aSelection);
+  const nsRange* range = selection->GetAnchorFocusRange();
+  nsINode* cntrNode = nullptr;
+  if (range)
+    cntrNode = range->GetCommonAncestor();
+  if (!cntrNode) {
+    cntrNode = selection->GetFrameSelection()->GetAncestorLimiter();
+    if (!cntrNode) {
+      cntrNode = selection->GetPresShell()->GetDocument();
+      NS_ASSERTION(selection->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(),
+                   "Wrong selection container was used!");
+    }
   }
 
-  mLastCaretOffset = caretOffset;
-  mLastTextAccessible = textAcc;
-
-  nsRefPtr<AccEvent> event = new AccCaretMoveEvent(mLastTextAccessible);
-  mLastTextAccessible->Document()->FireDelayedEvent(event);
-}
-
-void
-SelectionManager::SpellcheckSelectionChanged(nsISelection* aSelection)
-{
-  // XXX: fire an event for accessible of focus node of the selection. If
-  // spellchecking is enabled then we will fire the number of events for
-  // the same accessible for newly appended range of the selection (for every
-  // misspelled word). If spellchecking is disabled (for example,
-  // @spellcheck="false" on html:body) then we won't fire any event.
-
-  HyperTextAccessible* hyperText =
-    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
-  if (hyperText) {
-    hyperText->Document()->
-      FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
-                       hyperText);
-  }
-}
-
-nsIntRect
-SelectionManager::GetCaretRect(nsIWidget** aWidget)
-{
-  nsIntRect caretRect;
-  NS_ENSURE_TRUE(aWidget, caretRect);
-  *aWidget = nullptr;
-
-  if (!mLastTextAccessible) {
-    return caretRect;    // Return empty rect
+  HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
+  if (!text) {
+    NS_NOTREACHED("We must reach document accessible implementing text interface!");
+    return;
   }
 
-  nsINode *lastNodeWithCaret = mLastTextAccessible->GetNode();
-  NS_ENSURE_TRUE(lastNodeWithCaret, caretRect);
-
-  nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(lastNodeWithCaret);
-  NS_ENSURE_TRUE(presShell, caretRect);
-
-  nsRefPtr<nsCaret> caret = presShell->GetCaret();
-  NS_ENSURE_TRUE(caret, caretRect);
-
-  nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection));
-  NS_ENSURE_TRUE(caretSelection, caretRect);
-  
-  bool isVisible;
-  caret->GetCaretVisible(&isVisible);
-  if (!isVisible) {
-    return nsIntRect();  // Return empty rect
-  }
-
-  nsRect rect;
-  nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
-  if (!frame || rect.IsEmpty()) {
-    return nsIntRect(); // Return empty rect
-  }
+  if (selection->GetType() == nsISelectionController::SELECTION_NORMAL) {
+    nsRefPtr<AccEvent> event = new AccTextSelChangeEvent(text, aSelection);
+    text->Document()->FireDelayedEvent(event);
 
-  nsPoint offset;
-  // Offset from widget origin to the frame origin, which includes chrome
-  // on the widget.
-  *aWidget = frame->GetNearestWidget(offset);
-  NS_ENSURE_TRUE(*aWidget, nsIntRect());
-  rect.MoveBy(offset);
-
-  caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
-  // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
-  caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
-
-  // Correct for character size, so that caret always matches the size of the character
-  // This is important for font size transitions, and is necessary because the Gecko caret uses the
-  // previous character's size as the user moves forward in the text by character.
-  int32_t charX, charY, charWidth, charHeight;
-  if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY,
-                                                            &charWidth, &charHeight,
-                                                            nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
-    caretRect.height -= charY - caretRect.y;
-    caretRect.y = charY;
+  } else if (selection->GetType() == nsISelectionController::SELECTION_SPELLCHECK) {
+    // XXX: fire an event for container accessible of the focus/anchor range
+    // of the spelcheck selection.
+    text->Document()->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
+                                       text);
   }
-
-  return caretRect;
 }
--- a/accessible/src/base/SelectionManager.h
+++ b/accessible/src/base/SelectionManager.h
@@ -1,35 +1,30 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_SelectionManager_h__
 #define mozilla_a11y_SelectionManager_h__
 
-#include "nsAutoPtr.h"
 #include "nsIFrame.h"
 #include "nsISelectionListener.h"
 
-class nsIContent;
-class nsIntRect;
 class nsIPresShell;
-class nsIWeakReference;
-class nsIWidget;
 
 namespace mozilla {
 
 namespace dom {
 class Element;
 }
 
 namespace a11y {
 
-class HyperTextAccessible;
+class AccEvent;
 
 /**
  * This special accessibility class is for the caret and selection management.
  * There is only 1 visible caret per top level window. However, there may be
  * several visible selections.
  *
  * The important selections are the one owned by each document, and the one in
  * the currently focused control.
@@ -48,17 +43,17 @@ class SelectionManager : public nsISelec
 public:
   // nsISupports
   // implemented by derived nsAccessibilityService
 
   // nsISelectionListener
   NS_DECL_NSISELECTIONLISTENER
 
   // SelectionManager
-  void Shutdown();
+  void Shutdown() { ClearControlSelectionListener(); }
 
   /**
    * Listen to selection events on the focused control.
    *
    * Note: only one control's selection events are listened to at a time. This
    * will remove the previous control's selection listener.
    */
   void SetControlSelectionListener(dom::Element* aFocusedElm);
@@ -74,45 +69,28 @@ public:
   void AddDocSelectionListener(nsIPresShell* aPresShell);
 
   /**
    * Stop listening to selection events for a given document
    */
   void RemoveDocSelectionListener(nsIPresShell* aShell);
 
   /**
-   * Return the caret rect and the widget containing the caret.
+   * Process delayed event, results in caret move and text selection change
+   * events.
    */
-  nsIntRect GetCaretRect(nsIWidget** aWidget);
+  void ProcessTextSelChangeEvent(AccEvent* aEvent);
 
 protected:
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(nsISelection* aSelection);
 
-  /**
-   * Process normal selection change and fire caret move event.
-   */
-  void NormalSelectionChanged(nsISelection* aSelection);
-
-  /**
-   * Process spellcheck selection change and fire text attribute changed event
-   * for invalid text attribute.
-   */
-  void SpellcheckSelectionChanged(nsISelection* aSelection);
-
 private:
   // Currently focused control.
   nsWeakFrame mCurrCtrlFrame;
-
-  // Info for the the last selection event.
-  // If it was on a control, then its control's selection. Otherwise, it's for
-  // a document where the selection changed.
-  nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection
-  nsRefPtr<HyperTextAccessible> mLastTextAccessible;
-  int32_t mLastCaretOffset;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -247,52 +247,34 @@ bool
 nsAccUtils::IsARIASelected(Accessible* aAccessible)
 {
   return aAccessible->GetContent()->
     AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
                 nsGkAtoms::_true, eCaseMatters);
 }
 
 HyperTextAccessible*
-nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
+nsAccUtils::GetTextContainer(nsINode* aNode)
 {
-  // Get accessible from selection's focus DOM point (the DOM point where
-  // selection is ended).
-
-  nsCOMPtr<nsIDOMNode> focusDOMNode;
-  aSelection->GetFocusNode(getter_AddRefs(focusDOMNode));
-  if (!focusDOMNode)
+  // Get text accessible containing the result node.
+  DocAccessible* doc =
+    GetAccService()->GetDocAccessible(aNode->OwnerDoc());
+  Accessible* accessible =
+    doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
+  if (!accessible)
     return nullptr;
 
-  int32_t focusOffset = 0;
-  aSelection->GetFocusOffset(&focusOffset);
-
-  nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
-  nsCOMPtr<nsINode> resultNode =
-    nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
-
-  // Get text accessible containing the result node.
-  DocAccessible* doc = 
-    GetAccService()->GetDocAccessible(resultNode->OwnerDoc());
-  Accessible* accessible = doc ? 
-    doc->GetAccessibleOrContainer(resultNode) : nullptr;
-  if (!accessible) {
-    NS_NOTREACHED("No nsIAccessibleText for selection change event!");
-    return nullptr;
-  }
-
   do {
     HyperTextAccessible* textAcc = accessible->AsHyperText();
     if (textAcc)
       return textAcc;
 
     accessible = accessible->Parent();
   } while (accessible);
 
-  NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
   return nullptr;
 }
 
 nsIntPoint
 nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
                                   uint32_t aCoordinateType,
                                   Accessible* aAccessible)
 {
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -132,32 +132,27 @@ public:
    *
    * @param  aAccessible  [in] the item accessible
    * @param  aState       [in] the state of the item accessible
    */
   static Accessible* GetSelectableContainer(Accessible* aAccessible,
                                             uint64_t aState);
 
   /**
+   * Return a text container accessible for the given node.
+   */
+  static HyperTextAccessible* GetTextContainer(nsINode* aNode);
+
+  /**
    * Return true if the DOM node of given accessible has aria-selected="true"
    * attribute.
    */
   static bool IsARIASelected(Accessible* aAccessible);
 
   /**
-   * Return text accessible containing focus point of the given selection.
-   * Used for normal and misspelling selection changes processing.
-   *
-   * @param aSelection  [in] the given selection
-   * @return            text accessible
-   */
-  static HyperTextAccessible*
-    GetTextAccessibleFromSelection(nsISelection* aSelection);
-
-  /**
    * Converts the given coordinates to coordinates relative screen.
    *
    * @param aX               [in] the given x coord
    * @param aY               [in] the given y coord
    * @param aCoordinateType  [in] specifies coordinates origin (refer to
    *                         nsIAccessibleCoordinateType)
    * @param aAccessible      [in] the accessible if coordinates are given
    *                         relative it.
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -4,22 +4,24 @@
  * 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 "HyperTextAccessible.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsIAccessibleTypes.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
 #include "TextAttrs.h"
 #include "TreeWalker.h"
 
+#include "nsCaret.h"
 #include "nsIClipboard.h"
 #include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMRange.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsFrameSelection.h"
@@ -1737,16 +1739,67 @@ HyperTextAccessible::CaretLineNumber()
 
     caretFrame = parentFrame;
   }
 
   NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
   return lineNumber;
 }
 
+nsIntRect
+HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
+{
+  *aWidget = nullptr;
+
+  nsRefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret();
+  NS_ENSURE_TRUE(caret, nsIntRect());
+
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  NS_ENSURE_TRUE(caretSelection, nsIntRect());
+
+  bool isVisible = false;
+  caret->GetCaretVisible(&isVisible);
+  if (!isVisible)
+    return nsIntRect();
+
+  nsRect rect;
+  nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
+  if (!frame || rect.IsEmpty())
+    return nsIntRect();
+
+  nsPoint offset;
+  // Offset from widget origin to the frame origin, which includes chrome
+  // on the widget.
+  *aWidget = frame->GetNearestWidget(offset);
+  NS_ENSURE_TRUE(*aWidget, nsIntRect());
+  rect.MoveBy(offset);
+
+  nsIntRect caretRect;
+  caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
+  // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
+  caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
+
+  int32_t caretOffset = -1;
+  GetCaretOffset(&caretOffset);
+
+  // Correct for character size, so that caret always matches the size of
+  // the character. This is important for font size transitions, and is
+  // necessary because the Gecko caret uses the previous character's size as
+  // the user moves forward in the text by character.
+  int32_t charX = 0, charY = 0, charWidth = 0, charHeight = 0;
+  if (NS_SUCCEEDED(GetCharacterExtents(caretOffset, &charX, &charY,
+                                       &charWidth, &charHeight,
+                                       nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
+    caretRect.height -= charY - caretRect.y;
+    caretRect.y = charY;
+  }
+
+  return caretRect;
+}
+
 already_AddRefed<nsFrameSelection>
 HyperTextAccessible::FrameSelection()
 {
   nsIFrame* frame = GetFrame();
   return frame ? frame->GetFrameSelection() : nullptr;
 }
 
 void
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -257,16 +257,25 @@ public:
   }
 
   /**
    * Provide the line number for the caret.
    * @return 1-based index for the line number with the caret
    */
   int32_t CaretLineNumber();
 
+  /**
+   * Return the caret rect and the widget containing the caret within this
+   * text accessible.
+   *
+   * @param [out] the widget containing the caret
+   * @return      the caret rect
+   */
+  nsIntRect GetCaretRect(nsIWidget** aWidget);
+
   //////////////////////////////////////////////////////////////////////////////
   // EditableTextAccessible
 
   /**
    * Return the editor associated with the accessible.
    */
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -528,19 +528,17 @@ HTMLFileInputAccessible::HandleAccEvent(
       (event->GetState() == states::BUSY ||
        event->GetState() == states::REQUIRED ||
        event->GetState() == states::HASPOPUP ||
        event->GetState() == states::INVALID)) {
     Accessible* button = GetChildAt(0);
     if (button && button->Role() == roles::PUSHBUTTON) {
       nsRefPtr<AccStateChangeEvent> childEvent =
         new AccStateChangeEvent(button, event->GetState(),
-                                event->IsStateEnabled(),
-                                (event->IsFromUserInput() ? eFromUserInput
-                                                          : eNoUserInput));
+                                event->IsStateEnabled(), event->FromUserInput());
       nsEventShell::FireEvent(childEvent);
     }
   }
 
   return NS_OK;
 }
 
 
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -1127,17 +1127,17 @@ AccessibleWrap::HandleAccEvent(AccEvent*
   NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
 
   Accessible* accessible = aEvent->GetAccessible();
   if (!accessible)
     return NS_OK;
 
   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
       eventType == nsIAccessibleEvent::EVENT_FOCUS) {
-    UpdateSystemCaret();
+    UpdateSystemCaretFor(accessible);
   }
 
   int32_t childID = GetChildIDFor(accessible); // get the id for the accessible
   if (!childID)
     return NS_OK; // Can't fire an event without a child ID
 
   HWND hWnd = GetHWNDFor(accessible);
   NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
@@ -1281,30 +1281,29 @@ AccessibleWrap::GetXPAccessibleFor(const
     return nullptr;
   }
 
   // Gecko child indices are 0-based in contrast to indices used in MSAA.
   return GetChildAt(aVarChild.lVal - 1);
 }
 
 void
-AccessibleWrap::UpdateSystemCaret()
+AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible)
 {
   // Move the system caret so that Windows Tablet Edition and tradional ATs with 
   // off-screen model can follow the caret
   ::DestroyCaret();
 
-  a11y::RootAccessible* rootAccessible = RootAccessible();
-  if (!rootAccessible) {
+  HyperTextAccessible* text = aAccessible->AsHyperText();
+  if (!text)
     return;
-  }
 
   nsIWidget* widget = nullptr;
-  nsIntRect caretRect = SelectionMgr()->GetCaretRect(&widget);
-  HWND caretWnd; 
+  nsIntRect caretRect = text->GetCaretRect(&widget);
+  HWND caretWnd;
   if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
     return;
   }
 
   // Create invisible bitmap for caret, otherwise its appearance interferes
   // with Gecko caret
   HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr);
   if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) {  // Also destroys the last caret
--- a/accessible/src/windows/msaa/AccessibleWrap.h
+++ b/accessible/src/windows/msaa/AccessibleWrap.h
@@ -159,17 +159,17 @@ public: // construction, destruction
 
   /**
    * System caret support: update the Windows caret position. 
    * The system caret works more universally than the MSAA caret
    * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */
-  void UpdateSystemCaret();
+  void UpdateSystemCaretFor(Accessible* aAccessible);
 
   /**
    * Find an accessible by the given child ID in cached documents.
    */
   Accessible* GetXPAccessibleFor(const VARIANT& aVarChild);
 
   NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1205,16 +1205,24 @@ function synthDownKey(aNodeOrID, aChecke
  */
 function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs,
                                 aCheckerOrEventSeq);
 }
 
 /**
+ * Left arrow key invoker.
+ */
+function synthLeftKey(aNodeOrID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", null, aCheckerOrEventSeq);
+}
+
+/**
  * Right arrow key invoker.
  */
 function synthRightKey(aNodeOrID, aCheckerOrEventSeq)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq);
 }
 
 /**
@@ -1653,16 +1661,27 @@ function invokerChecker(aEventType, aTar
     return prettyName(this.mTarget);
   }
 
   this.mTarget = aTargetOrFunc;
   this.mTargetFuncArg = aTargetFuncArg;
 }
 
 /**
+ * Generic invoker checker for unexpected events.
+ */
+function unexpectedInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
+{
+  this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
+                                      aTargetFuncArg, true);
+
+  this.unexpected = true;
+}
+
+/**
  * Common invoker checker for async events.
  */
 function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
 {
   this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
                                       aTargetFuncArg, true);
 }
 
@@ -1732,16 +1751,29 @@ function caretMoveChecker(aCaretOffset, 
   {
     is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
        aCaretOffset,
        "Wrong caret offset for " + prettyName(aEvent.accessible));
   }
 }
 
 /**
+ * Text selection change checker.
+ */
+function textSelectionChecker(aID, aStartOffset, aEndOffset)
+{
+  this.__proto__ = new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID);
+
+  this.check = function textSelectionChecker_check(aEvent)
+  {
+    testTextGetSelection(aID, aStartOffset, aEndOffset, 0);
+  }
+}
+
+/**
  * State change checker.
  */
 function stateChangeChecker(aState, aIsExtraState, aIsEnabled,
                             aTargetOrFunc, aTargetFuncArg, aIsAsync,
                             aSkipCurrentStateCheck)
 {
   this.__proto__ = new invokerChecker(EVENT_STATE_CHANGE, aTargetOrFunc,
                                       aTargetFuncArg, aIsAsync);
--- a/accessible/tests/mochitest/events/a11y.ini
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -41,10 +41,11 @@ support-files =
 [test_scroll.xul]
 [test_selection.html]
 [test_selection.xul]
 [test_selection_aria.html]
 [test_statechange.html]
 [test_text.html]
 [test_text_alg.html]
 [test_textattrchange.html]
+[test_textselchange.html]
 [test_tree.xul]
 [test_valuechange.html]
--- a/accessible/tests/mochitest/events/test_caretmove.html
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -57,16 +57,20 @@
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
 
       id = "textarea";
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
       gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
 
+      id = "textarea_wrapped";
+      gQueue.push(new setCaretOffset(id, 4, id));
+      gQueue.push(new synthLeftKey(id, new caretMoveChecker(4, id)));
+
       id = "p";
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
       gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
 
       id = "p1_in_div";
       gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
 
@@ -100,16 +104,21 @@
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=454377"
      title="Accessible caret move events testing">
     Bug 454377
   </a>
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=567571"
+     title="caret-moved events missing at the end of a wrapped line of text">
+    Bug 567571
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=824901"
      title="HyperTextAccessible::DOMPointToHypertextOffset fails for node and offset equal to node child count">
     Bug 824901
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
@@ -119,11 +128,13 @@
   <p id="p" contentEditable="true"><span>text</span><br/>text</p>
   <div id="div" contentEditable="true"><p id="p1_in_div">text</p><p id="p2_in_div">text</p></div>
 
   <p contentEditable="true" id="test1"><span id="test1_span">text</span>ohoho</p>
   <p contentEditable="true" id="test2"><span><span id="test2_span">text</span></span>ohoho</p>
   <p contentEditable="true" id="test3"></p>
   <p contentEditable="true" id="test4"><span id="test4_span"></span></p>
 
+  <textarea id="textarea_wrapped" cols="5">hey friend</textarea>
+
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textselchange.html
@@ -0,0 +1,67 @@
+<html>
+
+<head>
+  <title>Accessible text selection change events testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../text.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    var gQueue = null;
+
+    // gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    function doTests()
+    {
+      // test caret move events and caret offsets
+      gQueue = new eventQueue();
+
+      var onclickSeq = [
+        new caretMoveChecker(0, "c1_p1"), 
+        new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, "c1_p1")
+      ];
+      gQueue.push(new synthClick("c1_p1", onclickSeq));
+      gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true }));
+      gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true }));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=762934"
+     title="Text selection change event has a wrong target when selection is spanned through several objects">
+    Bug 762934
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="c1" contentEditable="true">
+    <p id="c1_p1">paragraph</p>
+    <p id="c1_p2">paragraph</p>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/accessible/tests/mochitest/hittest/test_browser.html
+++ b/accessible/tests/mochitest/hittest/test_browser.html
@@ -11,16 +11,18 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // Hit testing. See bug #726097
+      getNode("hittest").scrollIntoView(true);
+
       var hititem = getAccessible("hititem");
       var hittest = getAccessible("hittest");
 
       var [hitX, hitY, hitWidth, hitHeight] = getBounds(hititem);
       var tgtX = hitX + hitWidth / 2;
       var tgtY = hitY + hitHeight / 2;
 
       var rootAcc = getRootAccessible();
--- a/accessible/tests/mochitest/hittest/test_menu.xul
+++ b/accessible/tests/mochitest/hittest/test_menu.xul
@@ -76,16 +76,18 @@
     function doTest()
     {
       if (LINUX) {
         ok(true, "No tests is running on Linux");
         SimpleTest.finish();
         return;
       }
 
+      getNode("mi_file1").scrollIntoView(true);
+
       gQueue = new eventQueue();
       gQueue.push(new openMenu("mi_file1", "mp_file1", "mi_file1.1"));
       gQueue.push(new openMenu("mi_file1.2", "mp_file1.2", "mi_file1.2.1"));
       gQueue.push(new closeMenu("mi_file1", "mi_file1.1", "mi_file1.2.1"));
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/textselection/test_general.html
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -84,17 +84,17 @@
       }
     }
 
     function removeSelection(aID)
     {
       this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
 
       this.eventSeq = [
-        new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+        new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, document)
       ];
 
       this.invoke = function removeSelection_invoke()
       {
         this.hyperText.removeSelection(0);
       }
 
       this.finalCheck = function removeSelection_finalCheck()
@@ -128,18 +128,17 @@
 
     var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new addSelection("paragraph", 1, 3));
       gQueue.push(new changeSelection("paragraph", 2, 4));
-      //gQueue.push(new removeSelection("paragraph"));
-      todo(false, "removeSelection doesn't fire text selection changed events, see bug bug 688124.");
+      gQueue.push(new removeSelection("paragraph"));
 
       gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox")));
       gQueue.push(new changeSelection("textbox", 1, 3));
 
       gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
       gQueue.push(new changeSelection("textarea", 1, 3));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
@@ -150,17 +149,22 @@
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126"
      title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases">
-    Mozilla Bug 688126
+    Bug 688126
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124"
+     title="no text selection changed event when selection is removed">
+    Bug 688124
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <p id="paragraph">hello</p>
   <input id="textbox" value="hello"/>
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1254,70 +1254,89 @@ window.addEventListener('ContentStart', 
       type: 'visible-audio-channel-changed',
       channel: aData
     });
     shell.visibleNormalAudioActive = (aData == 'normal');
 }, "visible-audio-channel-changed", false);
 })();
 
 (function recordingStatusTracker() {
-  let gRecordingActiveCount = 0;
+  // Recording status is tracked per process with following data structure:
+  // {<processId>: {count: <N>,
+  //                requestURL: <requestURL>,
+  //                isApp: <isApp>,
+  //                audioCount: <N>,
+  //                videoCount: <N>}}
   let gRecordingActiveProcesses = {};
 
   let recordingHandler = function(aSubject, aTopic, aData) {
-    let oldCount = gRecordingActiveCount;
-
-    let processId = (!aSubject) ? 'main'
-                                : aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
+    let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
+    let processId = (props.hasKey('childID')) ? props.get('childID')
+                                              : 'main';
     if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) {
-      gRecordingActiveProcesses[processId] = 0;
+      gRecordingActiveProcesses[processId] = {count: 0,
+                                              requestURL: props.get('requestURL'),
+                                              isApp: props.get('isApp'),
+                                              audioCount: 0,
+                                              videoCount: 0 };
     }
 
     let currentActive = gRecordingActiveProcesses[processId];
+    let wasActive = (currentActive['count'] > 0);
+    let wasAudioActive = (currentActive['audioCount'] > 0);
+    let wasVideoActive = (currentActive['videoCount'] > 0);
+
     switch (aData) {
       case 'starting':
-        gRecordingActiveCount++;
-        currentActive++;
+        currentActive['count']++;
+        currentActive['audioCount'] += (props.get('isAudio')) ? 1 : 0;
+        currentActive['videoCount'] += (props.get('isVideo')) ? 1 : 0;
         break;
       case 'shutdown':
-        // Bug 928206 will make shutdown be sent even if no starting.
-        if (currentActive > 0) {
-          gRecordingActiveCount--;
-          currentActive--;
-        }
+        currentActive['count']--;
+        currentActive['audioCount'] -= (props.get('isAudio')) ? 1 : 0;
+        currentActive['videoCount'] -= (props.get('isVideo')) ? 1 : 0;
         break;
       case 'content-shutdown':
-        gRecordingActiveCount -= currentActive;
-        currentActive = 0;
+        currentActive['count'] = 0;
+        currentActive['audioCount'] = 0;
+        currentActive['videoCount'] = 0;
         break;
     }
 
-    if (currentActive > 0) {
+    if (currentActive['count'] > 0) {
       gRecordingActiveProcesses[processId] = currentActive;
     } else {
       delete gRecordingActiveProcesses[processId];
     }
 
-    // We need to track changes from N <-> 0
-    if ((oldCount === 0 && gRecordingActiveCount > 0) ||
-        (gRecordingActiveCount === 0 && oldCount > 0)) {
+    // We need to track changes if any active state is changed.
+    let isActive = (currentActive['count'] > 0);
+    let isAudioActive = (currentActive['audioCount'] > 0);
+    let isVideoActive = (currentActive['videoCount'] > 0);
+    if ((isActive != wasActive) ||
+        (isAudioActive != wasAudioActive) ||
+        (isVideoActive != wasVideoActive)) {
       shell.sendChromeEvent({
         type: 'recording-status',
-        active: (gRecordingActiveCount > 0)
+        active: isActive,
+        requestURL: currentActive['requestURL'],
+        isApp: currentActive['isApp'],
+        isAudio: isAudioActive,
+        isVideo: isVideoActive
       });
     }
   };
   Services.obs.addObserver(recordingHandler, 'recording-device-events', false);
   Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false);
 
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     // send additional recording events if content process is being killed
-    let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
-    let childId = aSubject.get('childID');
-    if (gRecordingActiveProcesses.hasOwnProperty(childId) >= 0) {
+    let processId = aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
+    if (gRecordingActiveProcesses.hasOwnProperty(processId)) {
       Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown');
     }
   }, 'ipc:content-shutdown', false);
 })();
 
 (function volumeStateTracker() {
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1382113575000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1382112975000">
   <emItems>
       <emItem  blockID="i454" id="sqlmoz@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
@@ -1186,37 +1186,16 @@
                   </pluginItem>
       <pluginItem  blockID="p459">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 25" maxVersion="Java 7 Update 44" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
-      <pluginItem  blockID="p462">
-                  <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 45" maxVersion="Java 7 Update 45" severity="0" vulnerabilitystatus="2">
-                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="24.0" maxVersion="*" />
-                          </targetApplication>
-                  </versionRange>
-                  </pluginItem>
-      <pluginItem  blockID="p463">
-      <match name="name" exp="Java\(TM\) Platform SE 7 U45(\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
-                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="24.0" maxVersion="*" />
-                          </targetApplication>
-                  </versionRange>
-                  </pluginItem>
-      <pluginItem  blockID="p464">
-      <match name="name" exp="Java(\(TM\))? Plug-in 10\.45(\.[0-9]+)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
-                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="24.0" maxVersion="*" />
-                          </targetApplication>
-                  </versionRange>
-                  </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
                   </devices>
             <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.17.12.5896</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g36">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -94,17 +94,17 @@ let AboutHomeListener = {
     // Inject search engine and snippets URL.
     let docElt = doc.documentElement;
     // set the following attributes BEFORE searchEngineName, which triggers to
     // show the snippets when it's set.
     docElt.setAttribute("snippetsURL", aData.snippetsURL);
     if (aData.showKnowYourRights)
       docElt.setAttribute("showKnowYourRights", "true");
     docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
-    docElt.setAttribute("searchEngineName", Services.search.defaultEngine.name);
+    docElt.setAttribute("searchEngineName", aData.defaultEngineName);
   },
 
   onPageLoad: function() {
     let doc = content.document;
     if (doc.documentURI.toLowerCase() != "about:home" ||
         doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       return;
     }
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -222,16 +222,22 @@ Sanitizer.prototype = {
 
         // Clear last URL of the Open Web Location dialog
         var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);
         try {
           prefs.clearUserPref("general.open_location.last_url");
         }
         catch (e) { }
+
+        try {
+          var seer = Components.classes["@mozilla.org/network/seer;1"]
+                               .getService(Components.interfaces.nsINetworkSeer);
+          seer.reset();
+        } catch (e) { }
       },
 
       get canClear()
       {
         // bug 347231: Always allow clearing history due to dependencies on
         // the browser:purge-session-history notification. (like error console)
         return true;
       }
--- a/browser/devtools/profiler/cleopatra/js/ui.js
+++ b/browser/devtools/profiler/cleopatra/js/ui.js
@@ -535,26 +535,16 @@ HistogramView.prototype = {
         ctx.fillStyle = "green";
       } else if (isInRangeSelector) {
         ctx.fillStyle = "blue";
       } else {
         ctx.fillStyle = step.color;
       }
       var roundedHeight = Math.round(step.value * height);
       ctx.fillRect(step.x, height - roundedHeight, step.width, roundedHeight);
-      if (step.marker) {
-        var x = step.x + step.width + 2;
-        var endPoint = x + ctx.measureText(step.marker).width;
-        var lastDataPoint = this._histogramData[this._histogramData.length-1];
-        if (endPoint >= lastDataPoint.x + lastDataPoint.width) {
-          x -= endPoint - (lastDataPoint.x + lastDataPoint.width) - 1;
-        }
-        ctx.fillText(step.marker, x, 15 + ((markerCount % 2) == 0 ? 0 : 20));
-        markerCount++;
-      }
     }
 
     this._finishedRendering = true;
   },
   highlightedCallstackChanged: function HistogramView_highlightedCallstackChanged(highlightedCallstack) {
     this._scheduleRender(highlightedCallstack);
   },
   _isInRangeSelector: function HistogramView_isInRangeSelector(index) {
--- a/browser/devtools/shadereditor/panel.js
+++ b/browser/devtools/shadereditor/panel.js
@@ -28,16 +28,17 @@ ShaderEditorPanel.prototype = {
     if (!this.target.isRemote) {
       targetPromise = this.target.makeRemote();
     } else {
       targetPromise = promise.resolve(this.target);
     }
 
     return targetPromise
       .then(() => {
+        this.panelWin.gToolbox = this._toolbox;
         this.panelWin.gTarget = this.target;
         this.panelWin.gFront = new WebGLFront(this.target.client, this.target.form);
         return this.panelWin.startupShaderEditor();
       })
       .then(() => {
         this.isReady = true;
         this.emit("ready");
         return this;
--- a/browser/devtools/shadereditor/shadereditor.js
+++ b/browser/devtools/shadereditor/shadereditor.js
@@ -34,17 +34,17 @@ const DEFAULT_EDITOR_CONFIG = {
   mode: Editor.modes.text,
   lineNumbers: true,
   showAnnotationRuler: true
 };
 
 /**
  * The current target and the WebGL Editor front, set by this tool's host.
  */
-let gTarget, gFront;
+let gToolbox, gTarget, gFront;
 
 /**
  * Initializes the shader editor controller and views.
  */
 function startupShaderEditor() {
   return promise.all([
     EventsHandler.initialize(),
     ShadersListView.initialize(),
@@ -66,32 +66,44 @@ function shutdownShaderEditor() {
 /**
  * Functions handling target-related lifetime events.
  */
 let EventsHandler = {
   /**
    * Listen for events emitted by the current tab target.
    */
   initialize: function() {
+    this._onHostChanged = this._onHostChanged.bind(this);
     this._onWillNavigate = this._onWillNavigate.bind(this);
     this._onProgramLinked = this._onProgramLinked.bind(this);
+    gToolbox.on("host-changed", this._onHostChanged);
     gTarget.on("will-navigate", this._onWillNavigate);
     gFront.on("program-linked", this._onProgramLinked);
 
   },
 
   /**
    * Remove events emitted by the current tab target.
    */
   destroy: function() {
+    gToolbox.off("host-changed", this._onHostChanged);
     gTarget.off("will-navigate", this._onWillNavigate);
     gFront.off("program-linked", this._onProgramLinked);
   },
 
   /**
+   * Handles a host change event on the parent toolbox.
+   */
+  _onHostChanged: function() {
+    if (gToolbox.hostType == "side") {
+      $("#shaders-pane").removeAttribute("height");
+    }
+  },
+
+  /**
    * Called for each location change in the debugged tab.
    */
   _onWillNavigate: function() {
     gFront.setup();
 
     ShadersListView.empty();
     ShadersEditorsView.setText({ vs: "", fs: "" });
     $("#reload-notice").hidden = true;
@@ -167,16 +179,22 @@ let ShadersListView = Heritage.extend(Wi
         checkboxTooltip: L10N.getStr("shadersList.blackboxLabel")
       }
     });
 
     // Make sure there's always a selected item available.
     if (!this.selectedItem) {
       this.selectedIndex = 0;
     }
+
+    // Prevent this container from growing indefinitely in height when the
+    // toolbox is docked to the side.
+    if (gToolbox.hostType == "side" && this.itemCount == SHADERS_AUTOGROW_ITEMS) {
+      this._pane.setAttribute("height", this._pane.getBoundingClientRect().height);
+    }
   },
 
   /**
    * The select listener for the sources container.
    */
   _onShaderSelect: function({ detail: sourceItem }) {
     if (!sourceItem) {
       return;
--- a/browser/devtools/shadereditor/shadereditor.xul
+++ b/browser/devtools/shadereditor/shadereditor.xul
@@ -35,30 +35,30 @@
           pack="center"
           flex="1"
           hidden="true">
       <label id="requests-menu-waiting-notice-label"
              class="plain"
              value="&shaderEditorUI.emptyNotice;"/>
     </hbox>
 
-    <hbox id="content" flex="1" hidden="true">
+    <box id="content" class="devtools-responsive-container" flex="1" hidden="true">
       <vbox id="shaders-pane"/>
       <splitter class="devtools-side-splitter"/>
-      <hbox id="shaders-editors" flex="1">
+      <box id="shaders-editors" class="devtools-responsive-container" flex="1">
         <vbox flex="1">
           <vbox id="vs-editor" flex="1"/>
           <label id="vs-editor-label"
                  class="plain editor-label"
                  value="&shaderEditorUI.vertexShader;"/>
         </vbox>
         <splitter id="editors-splitter" class="devtools-side-splitter"/>
         <vbox flex="1">
           <vbox id="fs-editor" flex="1"/>
           <label id="fs-editor-label"
                  class="plain editor-label"
                  value="&shaderEditorUI.fragmentShader;"/>
         </vbox>
-      </hbox>
-    </hbox>
+      </box>
+    </box>
   </vbox>
 
 </window>
--- a/browser/metro/theme/platform.css
+++ b/browser/metro/theme/platform.css
@@ -682,17 +682,17 @@ arrowbox {
 .meta-section-container[viewstate="snapped"] .meta-section-title,
 .meta-section-container[viewstate="snapped"] richgrid {
   margin-top: @metro_spacing_xnormal@;
   padding: 0;
 }
 
 .meta-section > richgrid {
   opacity: 1;
-  transform: translateX(0) scale(1);
+  transform: translateX(0) translateY(0);
   transition-duration: 367ms;
   transition-delay: 500ms;
   transition-timing-function: @metro_animation_easing@;
 }
 
 .meta-section:nth-child(2) > richgrid {
   transition-delay: 600ms;
 }
@@ -700,17 +700,17 @@ arrowbox {
   transition-delay: 700ms;
 }
 .meta-section:nth-child(4) > richgrid {
   transition-delay: 800ms;
 }
 
 .meta-section > richgrid[fade] {
   opacity: 0;
-  transform: translateX(150px) scale(.9);
+  transform: translateX(150px) translateY(25px);
 }
 
 #start-container[viewstate="snapped"] .meta-section > richgrid {
   transition-property: none;
 }
 
 
 /* App bars ----------------------------------------------------------------- */
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -182,17 +182,18 @@ let AboutHome = {
     Components.utils.import("resource:///modules/sessionstore/SessionStore.jsm",
       wrapper);
     let ss = wrapper.SessionStore;
     ss.promiseInitialized.then(function() {
       let data = {
         showRestoreLastSession: ss.canRestoreLastSession,
         snippetsURL: AboutHomeUtils.snippetsURL,
         showKnowYourRights: AboutHomeUtils.showKnowYourRights,
-        snippetsVersion: AboutHomeUtils.snippetsVersion
+        snippetsVersion: AboutHomeUtils.snippetsVersion,
+        defaultEngineName: Services.search.defaultEngine.name
       };
 
       if (AboutHomeUtils.showKnowYourRights) {
         // Set pref to indicate we've shown the notification.
         let currentVersion = Services.prefs.getIntPref("browser.rights.version");
         Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
       }
 
--- a/browser/themes/shared/devtools/shadereditor.inc.css
+++ b/browser/themes/shared/devtools/shadereditor.inc.css
@@ -100,8 +100,37 @@
   color: #fff;
 }
 
 .editor-label[selected] {
   background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left;
   box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15),
               inset 0 -1px 0 hsla(210,40%,83%,.05);
 }
+
+/* Responsive sidebar */
+
+@media (max-width: 700px) {
+  #shaders-pane {
+    max-height: 60vh;
+  }
+
+  .side-menu-widget-container {
+    box-shadow: none !important;
+  }
+
+  .side-menu-widget-item-arrow {
+    background-image: none !important;
+  }
+
+  .devtools-side-splitter {
+    border-top-color: transparent !important;
+  }
+
+  .editor-label {
+    -moz-box-ordinal-group: 0;
+  }
+
+  .editor-label:not([selected]) {
+    border-top: 1px solid hsla(210,8%,5%,.25);
+    box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15);
+  }
+}
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -179,39 +179,39 @@ nsDOMFileReader::GetReadyState(uint16_t 
 {
   *aReadyState = ReadyState();
   return NS_OK;
 }
 
 JS::Value
 nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> result(aCx, JS::UndefinedValue());
+  JS::Rooted<JS::Value> result(aCx);
   aRv = GetResult(aCx, result.address());
   return result;
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::GetResult(JSContext* aCx, JS::Value* aResult)
 {
+  JS::Rooted<JS::Value> result(aCx);
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
-      JSObject* tmp = mResultArrayBuffer;
-      *aResult = OBJECT_TO_JSVAL(tmp);
+      result.setObject(*mResultArrayBuffer);
     } else {
-      *aResult = JSVAL_NULL;
+      result.setNull();
     }
-    if (!JS_WrapValue(aCx, aResult)) {
+    if (!JS_WrapValue(aCx, &result)) {
       return NS_ERROR_FAILURE;
     }
+    *aResult = result;
     return NS_OK;
   }
- 
+
   nsString tmpResult = mResult;
-  JS::Rooted<JS::Value> result(aCx);
   if (!xpc::StringToJsval(aCx, tmpResult, &result)) {
     return NS_ERROR_FAILURE;
   }
   *aResult = result;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -305,16 +305,18 @@ public:
 
   /**
    * Applies a new set of sandbox flags. These are merged with the sandbox
    * flags from our owning content's owning document with a logical OR, this
    * ensures that we can only add restrictions and never remove them.
    */
   void ApplySandboxFlags(uint32_t sandboxFlags);
 
+  void GetURL(nsString& aURL);
+
 private:
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Is this a frameloader for a bona fide <iframe mozbrowser> or
@@ -353,17 +355,16 @@ private:
   already_AddRefed<mozIApplication> GetContainingApp();
 
   /**
    * If we are an IPC frame, set mRemoteFrame. Otherwise, create and
    * initialize mDocShell.
    */
   nsresult MaybeCreateDocShell();
   nsresult EnsureMessageManager();
-  NS_HIDDEN_(void) GetURL(nsString& aURL);
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsRect& aRect);
 
   // Updates the subdocument position and size. This gets called only
   // when we have our own in-process DocShell.
   NS_HIDDEN_(nsresult) UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);
   nsresult CheckURILoad(nsIURI* aURI);
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -46,24 +46,36 @@
 #  undef SendMessage
 # endif
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
+static PLDHashOperator
+CycleCollectorTraverseListeners(const nsAString& aKey,
+                                nsTArray<nsMessageListenerInfo>* aListeners,
+                                void* aCb)
+{
+  nsCycleCollectionTraversalCallback* cb =
+    static_cast<nsCycleCollectionTraversalCallback*> (aCb);
+  uint32_t count = aListeners->Length();
+  for (uint32_t i = 0; i < count; ++i) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener");
+    cb->NoteXPCOMChild((*aListeners)[i].mStrongListener.get());
+  }
+  return PL_DHASH_NEXT;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
-  uint32_t count = tmp->mListeners.Length();
-  for (uint32_t i = 0; i < count; i++) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mStrongListener");
-    cb.NoteXPCOMChild(tmp->mListeners[i].mStrongListener.get());
-  }
+  tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners,
+                                static_cast<void*>(&cb));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
   tmp->mListeners.Clear();
   for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
     static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
       Disconnect(false);
@@ -241,100 +253,136 @@ SameProcessCpowHolder::ToObject(JSContex
 }
 
 // nsIMessageListenerManager
 
 NS_IMETHODIMP
 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
                                           nsIMessageListener* aListener)
 {
-  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
-  uint32_t len = mListeners.Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    if (mListeners[i].mMessage == message &&
-      mListeners[i].mStrongListener == aListener) {
-      return NS_OK;
+  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
+  if (!listeners) {
+    listeners = new nsTArray<nsMessageListenerInfo>();
+    mListeners.Put(aMessage, listeners);
+  } else {
+    uint32_t len = listeners->Length();
+    for (uint32_t i = 0; i < len; ++i) {
+      if ((*listeners)[i].mStrongListener == aListener) {
+        return NS_OK;
+      }
     }
   }
-  nsMessageListenerInfo* entry = mListeners.AppendElement();
+
+  nsMessageListenerInfo* entry = listeners->AppendElement();
   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
-  entry->mMessage = message;
   entry->mStrongListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
                                              nsIMessageListener* aListener)
 {
-  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
-  uint32_t len = mListeners.Length();
+  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
+  if (!listeners) {
+    return NS_OK;
+  }
+
+  uint32_t len = listeners->Length();
   for (uint32_t i = 0; i < len; ++i) {
-    if (mListeners[i].mMessage == message &&
-      mListeners[i].mStrongListener == aListener) {
-      mListeners.RemoveElementAt(i);
+    if ((*listeners)[i].mStrongListener == aListener) {
+      listeners->RemoveElementAt(i);
       return NS_OK;
     }
   }
   return NS_OK;
 }
 
+#ifdef DEBUG
+typedef struct
+{
+  nsCOMPtr<nsISupports> mCanonical;
+  nsWeakPtr mWeak;
+} CanonicalCheckerParams;
+
+static PLDHashOperator
+CanonicalChecker(const nsAString& aKey,
+                 nsTArray<nsMessageListenerInfo>* aListeners,
+                 void* aParams)
+{
+  CanonicalCheckerParams* params =
+    static_cast<CanonicalCheckerParams*> (aParams);
+
+  uint32_t count = aListeners->Length();
+  for (uint32_t i = 0; i < count; i++) {
+    if (!(*aListeners)[i].mWeakListener) {
+      continue;
+    }
+    nsCOMPtr<nsISupports> otherCanonical =
+      do_QueryReferent((*aListeners)[i].mWeakListener);
+    MOZ_ASSERT((params->mCanonical == otherCanonical) ==
+               (params->mWeak == (*aListeners)[i].mWeakListener));
+  }
+  return PL_DHASH_NEXT;
+}
+#endif
+
 NS_IMETHODIMP
 nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
                                               nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE);
 
 #ifdef DEBUG
   // It's technically possible that one object X could give two different
   // nsIWeakReference*'s when you do_GetWeakReference(X).  We really don't want
   // this to happen; it will break e.g. RemoveWeakMessageListener.  So let's
   // check that we're not getting ourselves into that situation.
   nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    if (!mListeners[i].mWeakListener) {
-      continue;
-    }
-
-    nsCOMPtr<nsISupports> otherCanonical =
-      do_QueryReferent(mListeners[i].mWeakListener);
-    MOZ_ASSERT((canonical == otherCanonical) ==
-               (weak == mListeners[i].mWeakListener));
-  }
+  CanonicalCheckerParams params;
+  params.mCanonical = canonical;
+  params.mWeak = weak;
+  mListeners.EnumerateRead(CanonicalChecker, (void*)&params);
 #endif
 
-  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
-  uint32_t len = mListeners.Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    if (mListeners[i].mMessage == message &&
-        mListeners[i].mWeakListener == weak) {
-      return NS_OK;
+  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
+  if (!listeners) {
+    listeners = new nsTArray<nsMessageListenerInfo>();
+    mListeners.Put(aMessage, listeners);
+  } else {
+    uint32_t len = listeners->Length();
+    for (uint32_t i = 0; i < len; ++i) {
+      if ((*listeners)[i].mWeakListener == weak) {
+        return NS_OK;
+      }
     }
   }
 
-  nsMessageListenerInfo* entry = mListeners.AppendElement();
-  entry->mMessage = message;
+  nsMessageListenerInfo* entry = listeners->AppendElement();
   entry->mWeakListener = weak;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
                                                  nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_OK);
 
-  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
-  uint32_t len = mListeners.Length();
+  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
+  if (!listeners) {
+    return NS_OK;
+  }
+
+  uint32_t len = listeners->Length();
   for (uint32_t i = 0; i < len; ++i) {
-    if (mListeners[i].mMessage == message &&
-        mListeners[i].mWeakListener == weak) {
-      mListeners.RemoveElementAt(i);
+    if ((*listeners)[i].mWeakListener == weak) {
+      listeners->RemoveElementAt(i);
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 // nsIFrameScriptLoader
@@ -787,143 +835,141 @@ nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       const StructuredCloneData* aCloneData,
                                       CpowHolder* aCpows,
                                       InfallibleTArray<nsString>* aJSONRetVal)
 {
   AutoSafeJSContext ctx;
+  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
+  if (listeners) {
 
-  if (mListeners.Length()) {
-    nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
     MMListenerRemover lr(this);
 
-    for (uint32_t i = 0; i < mListeners.Length(); ++i) {
+    for (uint32_t i = 0; i < listeners->Length(); ++i) {
       // Remove mListeners[i] if it's an expired weak listener.
       nsCOMPtr<nsISupports> weakListener;
-      if (mListeners[i].mWeakListener) {
-        weakListener = do_QueryReferent(mListeners[i].mWeakListener);
+      if ((*listeners)[i].mWeakListener) {
+        weakListener = do_QueryReferent((*listeners)[i].mWeakListener);
         if (!weakListener) {
-          mListeners.RemoveElementAt(i--);
+          listeners->RemoveElementAt(i--);
           continue;
         }
       }
 
-      if (mListeners[i].mMessage == name) {
-        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
-        if (weakListener) {
-          wrappedJS = do_QueryInterface(weakListener);
-        } else {
-          wrappedJS = do_QueryInterface(mListeners[i].mStrongListener);
-        }
+      nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
+      if (weakListener) {
+        wrappedJS = do_QueryInterface(weakListener);
+      } else {
+        wrappedJS = do_QueryInterface((*listeners)[i].mStrongListener);
+      }
+
+      if (!wrappedJS) {
+        continue;
+      }
+      JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject());
+      if (!object) {
+        continue;
+      }
+      JSAutoCompartment ac(ctx, object);
+
+      // The parameter for the listener function.
+      JS::Rooted<JSObject*> param(ctx,
+        JS_NewObject(ctx, nullptr, nullptr, nullptr));
+      NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
 
-        if (!wrappedJS) {
-          continue;
+      JS::Rooted<JS::Value> targetv(ctx);
+      JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
+      nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
+                                 nullptr, true);
+
+      JS::RootedObject cpows(ctx);
+      if (aCpows) {
+        if (!aCpows->ToObject(ctx, &cpows)) {
+          return NS_ERROR_UNEXPECTED;
         }
-        JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject());
-        if (!object) {
-          continue;
+      }
+
+      if (!cpows) {
+        cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr);
+        if (!cpows) {
+          return NS_ERROR_UNEXPECTED;
         }
-        JSAutoCompartment ac(ctx, object);
+      }
+
+      JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows));
 
-        // The parameter for the listener function.
-        JS::Rooted<JSObject*> param(ctx,
-          JS_NewObject(ctx, nullptr, nullptr, nullptr));
-        NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
+      JS::Rooted<JS::Value> json(ctx, JS::NullValue());
+      if (aCloneData && aCloneData->mDataLength &&
+          !ReadStructuredClone(ctx, *aCloneData, &json)) {
+        JS_ClearPendingException(ctx);
+        return NS_OK;
+      }
+      JS::Rooted<JSString*> jsMessage(ctx,
+        JS_NewUCStringCopyN(ctx,
+                            static_cast<const jschar*>(aMessage.BeginReading()),
+                            aMessage.Length()));
+      NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
+      JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE);
+      JS_DefineProperty(ctx, param, "name",
+                        STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
+      JS_DefineProperty(ctx, param, "sync",
+                        BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE);
+      JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
+      JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
+      JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE);
 
-        JS::Rooted<JS::Value> targetv(ctx);
-        JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
-        nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
-                                   nullptr, true);
+      JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue());
+
+      JS::Rooted<JS::Value> funval(ctx);
+      if (JS_ObjectIsCallable(ctx, object)) {
+        // If the listener is a JS function:
+        funval.setObject(*object);
 
-        JS::RootedObject cpows(ctx);
-        if (aCpows) {
-          if (!aCpows->ToObject(ctx, &cpows)) {
-            return NS_ERROR_UNEXPECTED;
-          }
+        // A small hack to get 'this' value right on content side where
+        // messageManager is wrapped in TabChildGlobal.
+        nsCOMPtr<nsISupports> defaultThisValue;
+        if (mChrome) {
+          defaultThisValue = do_QueryObject(this);
+        } else {
+          defaultThisValue = aTarget;
         }
+        JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
+        nsContentUtils::WrapNative(ctx, global, defaultThisValue,
+                                   &thisValue, nullptr, true);
+      } else {
+        // If the listener is a JS object which has receiveMessage function:
+        if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||
+            !funval.isObject())
+          return NS_ERROR_UNEXPECTED;
 
-        if (!cpows) {
-          cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr);
-          if (!cpows) {
-            return NS_ERROR_UNEXPECTED;
-          }
+        // Check if the object is even callable.
+        NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
+        thisValue.setObject(*object);
+      }
+
+      JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
+      JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
+
+      {
+        JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
+
+        JSAutoCompartment tac(ctx, thisObject);
+        if (!JS_WrapValue(ctx, &argv)) {
+          return NS_ERROR_UNEXPECTED;
         }
 
-        JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows));
-
-        JS::Rooted<JS::Value> json(ctx, JS::NullValue());
-        if (aCloneData && aCloneData->mDataLength &&
-            !ReadStructuredClone(ctx, *aCloneData, &json)) {
-          JS_ClearPendingException(ctx);
-          return NS_OK;
-        }
-        JS::Rooted<JSString*> jsMessage(ctx,
-          JS_NewUCStringCopyN(ctx,
-                              static_cast<const jschar*>(aMessage.BeginReading()),
-                              aMessage.Length()));
-        NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
-        JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "name",
-                          STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "sync",
-                          BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
-        JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE);
-
-        JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue());
-
-        JS::Rooted<JS::Value> funval(ctx);
-        if (JS_ObjectIsCallable(ctx, object)) {
-          // If the listener is a JS function:
-          funval.setObject(*object);
-
-          // A small hack to get 'this' value right on content side where
-          // messageManager is wrapped in TabChildGlobal.
-          nsCOMPtr<nsISupports> defaultThisValue;
-          if (mChrome) {
-            defaultThisValue = do_QueryObject(this);
-          } else {
-            defaultThisValue = aTarget;
-          }
-          JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
-          nsContentUtils::WrapNative(ctx, global, defaultThisValue,
-                                     &thisValue, nullptr, true);
-        } else {
-          // If the listener is a JS object which has receiveMessage function:
-          if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||
-              !funval.isObject())
-            return NS_ERROR_UNEXPECTED;
-
-          // Check if the object is even callable.
-          NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
-          thisValue.setObject(*object);
-        }
-
-        JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
-        JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
-
-        {
-          JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
-
-          JSAutoCompartment tac(ctx, thisObject);
-          if (!JS_WrapValue(ctx, argv.address())) {
-            return NS_ERROR_UNEXPECTED;
-          }
-
-          JS_CallFunctionValue(ctx, thisObject,
-                               funval, 1, argv.address(), rval.address());
-          if (aJSONRetVal) {
-            nsString json;
-            if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue,
-                             JSONCreator, &json)) {
-              aJSONRetVal->AppendElement(json);
-            }
+        JS_CallFunctionValue(ctx, thisObject,
+                             funval, 1, argv.address(), rval.address());
+        if (aJSONRetVal) {
+          nsString json;
+          if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue,
+                           JSONCreator, &json)) {
+            aJSONRetVal->AppendElement(json);
           }
         }
       }
     }
   }
   nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
                                                          aIsSync, aCloneData,
@@ -1006,77 +1052,97 @@ nsFrameMessageManager::Disconnect(bool a
   mOwnedCallback = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
 namespace {
 
-struct MessageManagerReferentCount {
-  MessageManagerReferentCount() : strong(0), weakAlive(0), weakDead(0) {}
-  size_t strong;
-  size_t weakAlive;
-  size_t weakDead;
-  nsCOMArray<nsIAtom> suspectMessages;
-  nsDataHashtable<nsPtrHashKey<nsIAtom>, uint32_t> messageCounter;
+struct MessageManagerReferentCount
+{
+  MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
+  size_t mStrong;
+  size_t mWeakAlive;
+  size_t mWeakDead;
+  nsTArray<nsString> mSuspectMessages;
+  nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter;
 };
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
+
+  static const size_t kSuspectReferentCount = 300;
 protected:
-  static const size_t kSuspectReferentCount = 300;
   void CountReferents(nsFrameMessageManager* aMessageManager,
                       MessageManagerReferentCount* aReferentCount);
 };
 
 NS_IMPL_ISUPPORTS1(MessageManagerReporter, nsIMemoryReporter)
 
-void
-MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
-                                       MessageManagerReferentCount* aReferentCount)
+static PLDHashOperator
+CollectMessageListenerData(const nsAString& aKey,
+                           nsTArray<nsMessageListenerInfo>* aListeners,
+                           void* aData)
 {
-  for (uint32_t i = 0; i < aMessageManager->mListeners.Length(); i++) {
-    const nsMessageListenerInfo& listenerInfo = aMessageManager->mListeners[i];
+  MessageManagerReferentCount* referentCount =
+    static_cast<MessageManagerReferentCount*>(aData);
+
+  uint32_t listenerCount = aListeners->Length();
+  if (!listenerCount) {
+    return PL_DHASH_NEXT;
+  }
 
+  nsString key(aKey);
+  uint32_t oldCount = 0;
+  referentCount->mMessageCounter.Get(key, &oldCount);
+  uint32_t currentCount = oldCount + listenerCount;
+  referentCount->mMessageCounter.Put(key, currentCount);
+
+  // Keep track of messages that have a suspiciously large
+  // number of referents (symptom of leak).
+  if (currentCount == MessageManagerReporter::kSuspectReferentCount) {
+    referentCount->mSuspectMessages.AppendElement(key);
+  }
+
+  for (uint32_t i = 0; i < listenerCount; ++i) {
+    const nsMessageListenerInfo& listenerInfo = (*aListeners)[i];
     if (listenerInfo.mWeakListener) {
       nsCOMPtr<nsISupports> referent =
         do_QueryReferent(listenerInfo.mWeakListener);
       if (referent) {
-        aReferentCount->weakAlive++;
+        referentCount->mWeakAlive++;
       } else {
-        aReferentCount->weakDead++;
+        referentCount->mWeakDead++;
       }
     } else {
-      aReferentCount->strong++;
-    }
-
-    uint32_t oldCount = 0;
-    aReferentCount->messageCounter.Get(listenerInfo.mMessage, &oldCount);
-    uint32_t currentCount = oldCount + 1;
-    aReferentCount->messageCounter.Put(listenerInfo.mMessage, currentCount);
-
-    // Keep track of messages that have a suspiciously large
-    // number of referents (symptom of leak).
-    if (currentCount == kSuspectReferentCount) {
-      aReferentCount->suspectMessages.AppendElement(listenerInfo.mMessage);
+      referentCount->mStrong++;
     }
   }
+  return PL_DHASH_NEXT;
+}
+
+void
+MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
+                                       MessageManagerReferentCount* aReferentCount)
+{
+  aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData,
+                                            aReferentCount);
 
   // Add referent count in child managers because the listeners
   // participate in messages dispatched from parent message manager.
-  for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); i++) {
+  for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
     CountReferents(mm, aReferentCount);
   }
 }
 
 NS_IMETHODIMP
 MessageManagerReporter::GetName(nsACString& aName)
@@ -1097,35 +1163,35 @@ ReportReferentCount(const char* aManager
       rv = aCb->Callback(EmptyCString(), _path,                               \
                          nsIMemoryReporter::KIND_OTHER,                       \
                          nsIMemoryReporter::UNITS_COUNT, _amount,             \
                          _desc, aClosure);                                    \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
   REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
-         aReferentCount.strong,
+         aReferentCount.mStrong,
          nsPrintfCString("The number of strong referents held by the message "
                          "manager in the %s manager.", aManagerType));
   REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
-         aReferentCount.weakAlive,
+         aReferentCount.mWeakAlive,
          nsPrintfCString("The number of weak referents that are still alive "
                          "held by the message manager in the %s manager.",
                          aManagerType));
   REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
-         aReferentCount.weakDead,
+         aReferentCount.mWeakDead,
          nsPrintfCString("The number of weak referents that are dead "
                          "held by the message manager in the %s manager.",
                          aManagerType));
 
-  for (uint32_t i = 0; i < aReferentCount.suspectMessages.Length(); i++) {
+  for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
     uint32_t totalReferentCount = 0;
-    aReferentCount.messageCounter.Get(aReferentCount.suspectMessages[i],
-                                      &totalReferentCount);
-    nsAtomCString suspect(aReferentCount.suspectMessages[i]);
+    aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i],
+                                       &totalReferentCount);
+    NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
     REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
                            aManagerType, suspect.get()), totalReferentCount,
            nsPrintfCString("A message in the %s message manager with a "
                            "suspiciously large number of referents (symptom "
                            "of a leak).", aManagerType));
   }
 
 #undef REPORT
@@ -1706,22 +1772,32 @@ NS_NewChildProcessMessageManager(nsISync
   }
   nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   nsFrameMessageManager::sChildProcessManager = mm;
   return CallQueryInterface(mm, aResult);
 }
 
+static PLDHashOperator
+CycleCollectorMarkListeners(const nsAString& aKey,
+                            nsTArray<nsMessageListenerInfo>* aListeners,
+                            void* aData)
+{
+  uint32_t count = aListeners->Length();
+  for (uint32_t i = 0; i < count; i++) {
+    if ((*aListeners)[i].mStrongListener) {
+      xpc_TryUnmarkWrappedGrayObject((*aListeners)[i].mStrongListener);
+    }
+  }
+  return PL_DHASH_NEXT;
+}
+
 bool
 nsFrameMessageManager::MarkForCC()
 {
-  uint32_t len = mListeners.Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    if (mListeners[i].mStrongListener) {
-      xpc_TryUnmarkWrappedGrayObject(mListeners[i].mStrongListener);
-    }
-  }
+  mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr);
+
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
   return true;
 }
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -13,16 +13,17 @@
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsIAtom.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsDataHashtable.h"
+#include "nsClassHashtable.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "js/RootingAPI.h"
 
 namespace mozilla {
@@ -111,17 +112,16 @@ StructuredCloneData UnpackClonedMessageD
 
 class nsAXPCNativeCallContext;
 
 struct nsMessageListenerInfo
 {
   // Exactly one of mStrongListener and mWeakListener must be non-null.
   nsCOMPtr<nsIMessageListener> mStrongListener;
   nsWeakPtr mWeakListener;
-  nsCOMPtr<nsIAtom> mMessage;
 };
 
 class CpowHolder
 {
   public:
     virtual bool ToObject(JSContext* cx, JS::MutableHandleObject objp) = 0;
 };
 
@@ -263,17 +263,19 @@ private:
                        const JS::Value& aJSON,
                        const JS::Value& aObjects,
                        JSContext* aCx,
                        uint8_t aArgc,
                        JS::Value* aRetval,
                        bool aIsSync);
 protected:
   friend class MMListenerRemover;
-  nsTArray<nsMessageListenerInfo> mListeners;
+  // We keep the message listeners as arrays in a hastable indexed by the
+  // message name. That gives us fast lookups in ReceiveMessage().
+  nsClassHashtable<nsStringHashKey, nsTArray<nsMessageListenerInfo>> mListeners;
   nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
   bool mChrome;     // true if we're in the chrome process
   bool mGlobal;     // true if we're the global frame message manager
   bool mIsProcessManager; // true if the message manager belongs to the process realm
   bool mIsBroadcaster; // true if the message manager is a broadcaster
   bool mOwnsCallback;
   bool mHandlingMessage;
   bool mDisconnected;
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3141,27 +3141,25 @@ nsObjectLoadingContent::LegacyCall(JSCon
     return JS::UndefinedValue();
   }
 
   obj = thisContent->GetWrapper();
   // Now wrap things up into the compartment of "obj"
   JSAutoCompartment ac(aCx, obj);
   nsTArray<JS::Value> args(aArguments);
   JS::AutoArrayRooter rooter(aCx, args.Length(), args.Elements());
-  for (JS::Value *arg = args.Elements(), *arg_end = arg + args.Length();
-       arg != arg_end;
-       ++arg) {
-    if (!JS_WrapValue(aCx, arg)) {
+  for (size_t i = 0; i < args.Length(); i++) {
+    if (!JS_WrapValue(aCx, rooter.handleAt(i))) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return JS::UndefinedValue();
     }
   }
 
   JS::Rooted<JS::Value> thisVal(aCx, aThisVal);
-  if (!JS_WrapValue(aCx, thisVal.address())) {
+  if (!JS_WrapValue(aCx, &thisVal)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return JS::UndefinedValue();
   }
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
   nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
@@ -3184,17 +3182,17 @@ nsObjectLoadingContent::LegacyCall(JSCon
   }
 
   if (!pi_obj) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return JS::UndefinedValue();
   }
 
   JS::Rooted<JS::Value> retval(aCx);
-  bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), args.Elements(),
+  bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), rooter.array,
                      &retval);
   if (!ok) {
     aRv.Throw(NS_ERROR_FAILURE);
     return JS::UndefinedValue();
   }
 
   Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
   return retval;
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -42,16 +42,17 @@
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "nsCRT.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozilla/dom/Element.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
+#include "nsINetworkSeer.h"
 
 #include "mozilla/CORSMode.h"
 #include "mozilla/Attributes.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
@@ -324,16 +325,20 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
   if (httpChannel) {
     // HTTP content negotation has little value in this context.
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                   NS_LITERAL_CSTRING("*/*"),
                                   false);
     httpChannel->SetReferrer(mDocument->GetDocumentURI());
   }
 
+  nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell));
+  mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(),
+      nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext);
+
   nsCOMPtr<nsIStreamLoader> loader;
   rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIStreamListener> listener = loader.get();
 
   if (aRequest->mCORSMode != CORS_NONE) {
     bool withCredentials = (aRequest->mCORSMode == CORS_USE_CREDENTIALS);
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -62,17 +62,17 @@ nsDOMMessageEvent::GetData(JSContext* aC
   *aData = GetData(aCx, rv);
   return rv.ErrorCode();
 }
 
 JS::Value
 nsDOMMessageEvent::GetData(JSContext* aCx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> data(aCx, mData);
-  if (!JS_WrapValue(aCx, data.address())) {
+  if (!JS_WrapValue(aCx, &data)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
   return data;
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::GetOrigin(nsAString& aOrigin)
 {
--- a/content/html/content/src/UndoManager.cpp
+++ b/content/html/content/src/UndoManager.cpp
@@ -1151,17 +1151,17 @@ UndoManager::DispatchTransactionEvent(JS
   nsCOMPtr<nsIWritableVariant> transactions = new nsVariant();
 
   // Unwrap the DOMTransactions into jsvals, then convert
   // to nsIVariant then put into a nsIVariant array. Arrays in XPIDL suck.
   nsCOMArray<nsIVariant> keepAlive;
   nsTArray<nsIVariant*> transactionItems;
   for (uint32_t i = 0; i < items.Length(); i++) {
     JS::Rooted<JS::Value> txVal(aCx, JS::ObjectValue(*items[i]->Callback()));
-    if (!JS_WrapValue(aCx, txVal.address())) {
+    if (!JS_WrapValue(aCx, &txVal)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     nsCOMPtr<nsIVariant> txVariant;
     nsresult rv =
       nsContentUtils::XPConnect()->JSToVariant(aCx, txVal,
                                                getter_AddRefs(txVariant));
     if (NS_SUCCEEDED(rv)) {
--- a/content/media/AudioSegment.cpp
+++ b/content/media/AudioSegment.cpp
@@ -150,19 +150,22 @@ AudioSegment::WriteTo(uint64_t aID, Audi
                                      duration, c.mVolume,
                                      outputChannels,
                                      buf.Elements());
         }
       } else {
         // Assumes that a bit pattern of zeroes == 0.0f
         memset(buf.Elements(), 0, buf.Length()*sizeof(AudioDataValue));
       }
-      aOutput->Write(buf.Elements(), int32_t(duration));
-      if(!c.mTimeStamp.IsNull())
-        LogLatency(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
-                   (mozilla::TimeStamp::Now() - c.mTimeStamp).ToMilliseconds());
+      aOutput->Write(buf.Elements(), int32_t(duration), &(c.mTimeStamp));
+      if(!c.mTimeStamp.IsNull()) {
+        TimeStamp now = TimeStamp::Now();
+        // would be more efficient to c.mTimeStamp to ms on create time then pass here
+        LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
+                (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
+      }
       offset += duration;
     }
   }
   aOutput->Start();
 }
 
 }
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -138,17 +138,18 @@ static cubeb_stream_type ConvertChannelT
 }
 #endif
 
 AudioStream::AudioStream()
 : mInRate(0),
   mOutRate(0),
   mChannels(0),
   mWritten(0),
-  mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST())
+  mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST()),
+  mReadPoint(0)
 {}
 
 void AudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   gAudioPrefsLock = new Mutex("AudioStream::gAudioPrefsLock");
@@ -323,28 +324,29 @@ class BufferedAudioStream : public Audio
  public:
   BufferedAudioStream();
   ~BufferedAudioStream();
 
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannelType aAudioChannelType,
                 AudioStream::LatencyRequest aLatencyRequest);
   void Shutdown();
-  nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
+  nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr);
   uint32_t Available();
   void SetVolume(double aVolume);
   void Drain();
   void Start();
   void Pause();
   void Resume();
   int64_t GetPosition();
   int64_t GetPositionInFrames();
   int64_t GetPositionInFramesInternal();
   int64_t GetLatencyInFrames();
   bool IsPaused();
+  void GetBufferInsertTime(int64_t &aTimeMs);
   // This method acquires the monitor and forward the call to the base
   // class, to prevent a race on |mTimeStretcher|, in
   // |AudioStream::EnsureTimeStretcherInitialized|.
   nsresult EnsureTimeStretcherInitialized();
 
 private:
   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
   {
@@ -354,20 +356,19 @@ private:
   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
   {
     static_cast<BufferedAudioStream*>(aThis)->StateCallback(aState);
   }
 
   long DataCallback(void* aBuffer, long aFrames);
   void StateCallback(cubeb_state aState);
 
-  long GetUnprocessed(void* aBuffer, long aFrames);
-
-  long GetTimeStretched(void* aBuffer, long aFrames);
-
+  // aTime is the time in ms the samples were inserted into MediaStreamGraph
+  long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
+  long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
 
   // Shared implementation of underflow adjusted position calculation.
   // Caller must own the monitor.
   int64_t GetPositionInFramesUnlocked();
 
   void StartUnlocked();
 
   // The monitor is held to protect all access to member variables.  Write()
@@ -537,26 +538,26 @@ WriteDumpFile(FILE* aDumpFile, AudioStre
   fwrite(output, 2, samples, aDumpFile);
   fflush(aDumpFile);
 }
 
 BufferedAudioStream::BufferedAudioStream()
   : mMonitor("BufferedAudioStream"), mLostFrames(0), mDumpFile(nullptr),
     mVolume(1.0), mBytesPerFrame(0), mState(INITIALIZED)
 {
-  AsyncLatencyLogger::Get(true)->AddRef();
+  // keep a ref in case we shut down later than nsLayoutStatics
+  mLatencyLog = AsyncLatencyLogger::Get(true);
 }
 
 BufferedAudioStream::~BufferedAudioStream()
 {
   Shutdown();
   if (mDumpFile) {
     fclose(mDumpFile);
   }
-  AsyncLatencyLogger::Get()->Release();
 }
 
 nsresult
 BufferedAudioStream::EnsureTimeStretcherInitialized()
 {
   MonitorAutoLock mon(mMonitor);
   return AudioStream::EnsureTimeStretcherInitialized();
 }
@@ -567,16 +568,18 @@ BufferedAudioStream::Init(int32_t aNumCh
                           AudioStream::LatencyRequest aLatencyRequest)
 {
   cubeb* cubebContext = GetCubebContext();
 
   if (!cubebContext || aNumChannels < 0 || aRate < 0) {
     return NS_ERROR_FAILURE;
   }
 
+  PR_LOG(gAudioStreamLog, PR_LOG_DEBUG,
+    ("%s  channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate));
   mInRate = mOutRate = aRate;
   mChannels = aNumChannels;
 
   mDumpFile = OpenDumpFile(this);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = aNumChannels;
@@ -640,29 +643,46 @@ BufferedAudioStream::Shutdown()
   if (mState == STARTED) {
     Pause();
   }
   if (mCubebStream) {
     mCubebStream.reset();
   }
 }
 
+// aTime is the time in ms the samples were inserted into MediaStreamGraph
 nsresult
-BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
+BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime)
 {
   MonitorAutoLock mon(mMonitor);
   if (!mCubebStream || mState == ERRORED) {
     return NS_ERROR_FAILURE;
   }
   NS_ASSERTION(mState == INITIALIZED || mState == STARTED,
     "Stream write in unexpected state.");
 
   const uint8_t* src = reinterpret_cast<const uint8_t*>(aBuf);
   uint32_t bytesToCopy = FramesToBytes(aFrames);
 
+  // XXX this will need to change if we want to enable this on-the-fly!
+  if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+    // Record the position and time this data was inserted
+    int64_t timeMs;
+    if (aTime && !aTime->IsNull()) {
+      if (mStartTime.IsNull()) {
+        AsyncLatencyLogger::Get(true)->GetStartTime(mStartTime);
+      }
+      timeMs = (*aTime - mStartTime).ToMilliseconds();
+    } else {
+      timeMs = 0;
+    }
+    struct Inserts insert = { timeMs, aFrames};
+    mInserts.AppendElement(insert);
+  }
+
   while (bytesToCopy > 0) {
     uint32_t available = std::min(bytesToCopy, mBuffer.Available());
     NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0,
         "Must copy complete frames.");
 
     mBuffer.AppendElements(src, available);
     src += available;
     bytesToCopy -= available;
@@ -841,18 +861,35 @@ BufferedAudioStream::GetLatencyInFrames(
 
 bool
 BufferedAudioStream::IsPaused()
 {
   MonitorAutoLock mon(mMonitor);
   return mState == STOPPED;
 }
 
+void
+BufferedAudioStream::GetBufferInsertTime(int64_t &aTimeMs)
+{
+  if (mInserts.Length() > 0) {
+    // Find the right block, but don't leave the array empty
+    while (mInserts.Length() > 1 && mReadPoint >= mInserts[0].mFrames) {
+      mReadPoint -= mInserts[0].mFrames;
+      mInserts.RemoveElementAt(0);
+    }
+    // offset for amount already read
+    // XXX Note: could misreport if we couldn't find a block in the right timeframe
+    aTimeMs = mInserts[0].mTimeMs + ((mReadPoint * 1000) / mOutRate);
+  } else {
+    aTimeMs = INT64_MAX;
+  }
+}
+
 long
-BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames)
+BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
 
   // Flush the timestretcher pipeline, if we were playing using a playback rate
   // other than 1.0.
   uint32_t flushedFrames = 0;
   if (mTimeStretcher && mTimeStretcher->numSamples()) {
     flushedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames);
@@ -862,21 +899,26 @@ BufferedAudioStream::GetUnprocessed(void
   uint32_t available = std::min(toPopBytes, mBuffer.Length());
 
   void* input[2];
   uint32_t input_size[2];
   mBuffer.PopElements(available, &input[0], &input_size[0], &input[1], &input_size[1]);
   memcpy(wpos, input[0], input_size[0]);
   wpos += input_size[0];
   memcpy(wpos, input[1], input_size[1]);
+
+  // First time block now has our first returned sample
+  mReadPoint += BytesToFrames(available);
+  GetBufferInsertTime(aTimeMs);
+
   return BytesToFrames(available) + flushedFrames;
 }
 
 long
-BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames)
+BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
   long processedFrames = 0;
 
   // We need to call the non-locking version, because we already have the lock.
   if (AudioStream::EnsureTimeStretcherInitialized() != NS_OK) {
     return 0;
   }
 
@@ -891,77 +933,87 @@ BufferedAudioStream::GetTimeStretched(vo
       void* input[2];
       uint32_t input_size[2];
       available = std::min(mBuffer.Length(), toPopBytes);
       if (available != toPopBytes) {
         lowOnBufferedData = true;
       }
       mBuffer.PopElements(available, &input[0], &input_size[0],
                                      &input[1], &input_size[1]);
+      mReadPoint += BytesToFrames(available);
       for(uint32_t i = 0; i < 2; i++) {
         mTimeStretcher->putSamples(reinterpret_cast<AudioDataValue*>(input[i]), BytesToFrames(input_size[i]));
       }
     }
     uint32_t receivedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames - processedFrames);
     wpos += FramesToBytes(receivedFrames);
     processedFrames += receivedFrames;
   } while (processedFrames < aFrames && !lowOnBufferedData);
 
+  GetBufferInsertTime(aTimeMs);
+
   return processedFrames;
 }
 
 long
 BufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
 {
   MonitorAutoLock mon(mMonitor);
   uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
   NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
+  AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer);
   uint32_t underrunFrames = 0;
   uint32_t servicedFrames = 0;
+  int64_t insertTime;
 
   if (available) {
-    AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer);
     if (mInRate == mOutRate) {
-      servicedFrames = GetUnprocessed(output, aFrames);
+      servicedFrames = GetUnprocessed(output, aFrames, insertTime);
     } else {
-      servicedFrames = GetTimeStretched(output, aFrames);
+      servicedFrames = GetTimeStretched(output, aFrames, insertTime);
     }
     float scaled_volume = float(GetVolumeScale() * mVolume);
 
     ScaleAudioSamples(output, aFrames * mChannels, scaled_volume);
 
     NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
 
     // Notify any blocked Write() call that more space is available in mBuffer.
     mon.NotifyAll();
+  } else {
+    GetBufferInsertTime(insertTime);
   }
 
   underrunFrames = aFrames - servicedFrames;
 
   if (mState != DRAINING) {
     uint8_t* rpos = static_cast<uint8_t*>(aBuffer) + FramesToBytes(aFrames - underrunFrames);
     memset(rpos, 0, FramesToBytes(underrunFrames));
-#ifdef PR_LOGGING
     if (underrunFrames) {
       PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
              ("AudioStream %p lost %d frames", this, underrunFrames));
     }
-#endif
     mLostFrames += underrunFrames;
     servicedFrames += underrunFrames;
   }
 
   WriteDumpFile(mDumpFile, this, aFrames, aBuffer);
-  if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+  // Don't log if we're not interested or if the stream is inactive
+  if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG) &&
+      insertTime != INT64_MAX && servicedFrames > underrunFrames) {
     uint32_t latency = UINT32_MAX;
     if (cubeb_stream_get_latency(mCubebStream, &latency)) {
       NS_WARNING("Could not get latency from cubeb.");
     }
-    mLatencyLog->Log(AsyncLatencyLogger::AudioStream, 0, (mBuffer.Length() * 1000) / mOutRate);
-    mLatencyLog->Log(AsyncLatencyLogger::Cubeb, 0, (latency * 1000) / mOutRate);
+    TimeStamp now = TimeStamp::Now();
+
+    mLatencyLog->Log(AsyncLatencyLogger::AudioStream, reinterpret_cast<uint64_t>(this),
+                     insertTime, now);
+    mLatencyLog->Log(AsyncLatencyLogger::Cubeb, reinterpret_cast<uint64_t>(mCubebStream.get()),
+                     (latency * 1000) / mOutRate, now);
   }
 
   mAudioClock.UpdateWritePosition(servicedFrames);
   return servicedFrames;
 }
 
 void
 BufferedAudioStream::StateCallback(cubeb_state aState)
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -129,18 +129,19 @@ public:
                         LatencyRequest aLatencyRequest) = 0;
 
   // Closes the stream. All future use of the stream is an error.
   virtual void Shutdown() = 0;
 
   // Write audio data to the audio hardware.  aBuf is an array of AudioDataValues
   // AudioDataValue of length aFrames*mChannels.  If aFrames is larger
   // than the result of Available(), the write will block until sufficient
-  // buffer space is available.
-  virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames) = 0;
+  // buffer space is available.  aTime is the time in ms associated with the first sample
+  // for latency calculations
+  virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr) = 0;
 
   // Return the number of audio frames that can be written without blocking.
   virtual uint32_t Available() = 0;
 
   // Set the current volume of the audio playback. This is a value from
   // 0 (meaning muted) to 1 (meaning full volume).  Thread-safe.
   virtual void SetVolume(double aVolume) = 0;
 
@@ -198,13 +199,26 @@ protected:
   // Output rate in Hz (characteristic of the playback rate)
   int mOutRate;
   int mChannels;
   // Number of frames written to the buffers.
   int64_t mWritten;
   AudioClock mAudioClock;
   nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
+
+  // copy of Latency logger's starting time for offset calculations
+  TimeStamp mStartTime;
+  // Where in the current mInserts[0] block cubeb has read to
+  int64_t mReadPoint;
+  // Keep track of each inserted block of samples and the time it was inserted
+  // so we can estimate the clock time for a specific sample's insertion (for when
+  // we send data to cubeb).  Blocks are aged out as needed.
+  struct Inserts {
+    int64_t mTimeMs;
+    int64_t mFrames;
+  };
+  nsAutoTArray<Inserts,8> mInserts;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/Latency.cpp
+++ b/content/media/Latency.cpp
@@ -19,17 +19,28 @@
 
 using namespace mozilla;
 
 const char* LatencyLogIndex2Strings[] = {
   "Audio MediaStreamTrack",
   "Video MediaStreamTrack",
   "Cubeb",
   "AudioStream",
-  "NetStat"
+  "NetEQ",
+  "AudioCapture Base",
+  "AudioCapture Samples",
+  "AudioTrackInsertion",
+  "MediaPipeline Audio Insertion",
+  "AudioTransmit",
+  "AudioReceive",
+  "MediaPipelineAudioPlayout",
+  "MediaStream Create",
+  "AudioStream Create",
+  "AudioSendRTP",
+  "AudioRecvRTP"
 };
 
 static StaticRefPtr<AsyncLatencyLogger> gAsyncLogger;
 
 PRLogModuleInfo*
 GetLatencyLog()
 {
   static PRLogModuleInfo* sLog;
@@ -38,39 +49,72 @@ GetLatencyLog()
   }
   return sLog;
 }
 
 
 class LogEvent : public nsRunnable
 {
 public:
+  LogEvent(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue,
+           TimeStamp aTimeStamp) :
+    mIndex(aIndex),
+    mID(aID),
+    mValue(aValue),
+    mTimeStamp(aTimeStamp)
+  {}
   LogEvent(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) :
     mIndex(aIndex),
     mID(aID),
-    mValue(aValue)
+    mValue(aValue),
+    mTimeStamp(TimeStamp())
   {}
   ~LogEvent() {}
 
   NS_IMETHOD Run() {
-    AsyncLatencyLogger::Get(true)->WriteLog(mIndex, mID, mValue);
+    AsyncLatencyLogger::Get(true)->WriteLog(mIndex, mID, mValue, mTimeStamp);
     return NS_OK;
   }
 
 protected:
   AsyncLatencyLogger::LatencyLogIndex mIndex;
   uint64_t mID;
   int64_t mValue;
+  TimeStamp mTimeStamp;
 };
 
 void LogLatency(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
 {
   AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue);
 }
 
+void LogTime(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
+{
+  TimeStamp now = TimeStamp::Now();
+  AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue, now);
+}
+
+void LogTime(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime)
+{
+  AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue, aTime);
+}
+
+void LogTime(uint32_t aIndex, uint64_t aID, int64_t aValue)
+{
+  LogTime(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue);
+}
+void LogTime(uint32_t aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime)
+{
+  LogTime(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue, aTime);
+}
+void LogLatency(uint32_t aIndex, uint64_t aID, int64_t aValue)
+{
+  LogLatency(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue);
+}
+
 /* static */
 void AsyncLatencyLogger::InitializeStatics()
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
   GetLatencyLog();
   gAsyncLogger = new AsyncLatencyLogger();
 }
 
@@ -123,42 +167,62 @@ void AsyncLatencyLogger::Init()
   MutexAutoLock lock(mMutex);
   if (mStart.IsNull()) {
     nsresult rv = NS_NewNamedThread("Latency Logger", getter_AddRefs(mThread));
     NS_ENSURE_SUCCESS_VOID(rv);
     mStart = TimeStamp::Now();
   }
 }
 
+void AsyncLatencyLogger::GetStartTime(TimeStamp &aStart)
+{
+  MutexAutoLock lock(mMutex);
+  aStart = mStart;
+}
+
 nsresult
 AsyncLatencyLogger::Observe(nsISupports* aSubject, const char* aTopic,
                             const PRUnichar* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     Shutdown();
   }
   return NS_OK;
 }
 
 // aID is a sub-identifier (in particular a specific MediaStramTrack)
-void AsyncLatencyLogger::WriteLog(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
+void AsyncLatencyLogger::WriteLog(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue,
+                                  TimeStamp aTimeStamp)
 {
-  PR_LOG(GetLatencyLog(), PR_LOG_DEBUG,
-         ("%s,%llu,%lld.,%lld.",
-          LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue));
+  if (aTimeStamp.IsNull()) {
+    PR_LOG(GetLatencyLog(), PR_LOG_DEBUG,
+      ("Latency: %s,%llu,%lld,%lld",
+       LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue));
+  } else {
+    PR_LOG(GetLatencyLog(), PR_LOG_DEBUG,
+      ("Latency: %s,%llu,%lld,%lld,%lld",
+       LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue,
+       static_cast<int64_t>((aTimeStamp - gAsyncLogger->mStart).ToMilliseconds())));
+  }
 }
 
 int64_t AsyncLatencyLogger::GetTimeStamp()
 {
   TimeDuration t = TimeStamp::Now() - mStart;
   return t.ToMilliseconds();
 }
 
 void AsyncLatencyLogger::Log(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
 {
+  TimeStamp null;
+  Log(aIndex, aID, aValue, null);
+}
+
+void AsyncLatencyLogger::Log(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime)
+{
   if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
-    nsCOMPtr<nsIRunnable> event = new LogEvent(aIndex, aID, aValue);
+    nsCOMPtr<nsIRunnable> event = new LogEvent(aIndex, aID, aValue, aTime);
     if (mThread) {
       mThread->Dispatch(event, NS_DISPATCH_NORMAL);
     }
   }
 }
--- a/content/media/Latency.h
+++ b/content/media/Latency.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_LATENCY_H
 #define MOZILLA_LATENCY_H
 
 #include "mozilla/TimeStamp.h"
-#include "prlog.h"
+#include "nspr/prlog.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "mozilla/Monitor.h"
 #include "nsISupportsImpl.h"
 #include "nsObserverService.h"
 
 class AsyncLatencyLogger;
 class LogEvent;
@@ -24,25 +24,44 @@ PRLogModuleInfo* GetLatencyLog();
 class AsyncLatencyLogger : public nsIObserver
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
 public:
 
   enum LatencyLogIndex {
-    AudioMediaStreamTrack,
+    AudioMediaStreamTrack = 0,
     VideoMediaStreamTrack,
     Cubeb,
     AudioStream,
     NetEQ,
+    AudioCaptureBase, // base time for capturing an audio stream
+    AudioCapture, // records number of samples captured and the time
+    AudioTrackInsertion, // # of samples inserted into a mediastreamtrack and the time
+    MediaPipelineAudioInsertion, // Timestamp and time of timestamp
+    AudioTransmit, // Timestamp and socket send time
+    AudioReceive, // Timestamp and receive time
+    MediaPipelineAudioPlayout, // Timestamp and playout into MST time
+    MediaStreamCreate, // Source and TrackUnion streams
+    AudioStreamCreate, // TrackUnion stream and AudioStream
+    AudioSendRTP,
+    AudioRecvRTP,
     _MAX_INDEX
   };
-  void Log(LatencyLogIndex index, uint64_t aID, int64_t value);
-  void WriteLog(LatencyLogIndex index, uint64_t aID, int64_t value);
+  // Log with a null timestamp
+  void Log(LatencyLogIndex index, uint64_t aID, int64_t aValue);
+  // Log with a timestamp
+  void Log(LatencyLogIndex index, uint64_t aID, int64_t aValue,
+           mozilla::TimeStamp &aTime);
+  // Write a log message to NSPR
+  void WriteLog(LatencyLogIndex index, uint64_t aID, int64_t aValue,
+                mozilla::TimeStamp timestamp);
+  // Get the base time used by the logger for delta calculations
+  void GetStartTime(mozilla::TimeStamp &aStart);
 
   static AsyncLatencyLogger* Get(bool aStartTimer = false);
   static void InitializeStatics();
   // After this is called, the global log object may go away
   static void ShutdownLogger();
 private:
   AsyncLatencyLogger();
   virtual ~AsyncLatencyLogger();
@@ -58,11 +77,24 @@ private:
   // thread only.
   mozilla::TimeStamp mStart;
   // This monitor protects mStart and mMediaLatencyLog for the
   // initialization sequence. It is initialized at layout startup, and
   // destroyed at layout shutdown.
   mozilla::Mutex mMutex;
 };
 
-void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t value);
+// need uint32_t versions for access from webrtc/trunk code
+// Log without a time delta
+void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue);
+void LogLatency(uint32_t index, uint64_t aID, int64_t aValue);
+// Log TimeStamp::Now() (as delta)
+void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue);
+void LogTime(uint32_t index, uint64_t aID, int64_t aValue);
+// Log the specified time (as delta)
+void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue,
+             mozilla::TimeStamp &aTime);
+
+// For generating unique-ish ids for logged sources
+#define LATENCY_STREAM_ID(source, trackID) \
+  ((((uint64_t) (source)) & ~0x0F) | (trackID))
 
 #endif
--- a/content/media/MediaSegment.h
+++ b/content/media/MediaSegment.h
@@ -228,16 +228,23 @@ public:
     MediaSegmentBase<C, Chunk>& mSegment;
     uint32_t mIndex;
   };
 
   void RemoveLeading(TrackTicks aDuration)
   {
     RemoveLeading(aDuration, 0);
   }
+
+#ifdef MOZILLA_INTERNAL_API
+  void GetStartTime(TimeStamp &aTime) {
+    aTime = mChunks[0].mTimeStamp;
+  }
+#endif
+
 protected:
   MediaSegmentBase(Type aType) : MediaSegment(aType) {}
 
   /**
    * Appends the contents of aSource to this segment, clearing aSource.
    */
   void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
   {
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -767,16 +767,20 @@ MediaStreamGraphImpl::CreateOrDestroyAud
           aStream->mAudioOutputStreams.AppendElement();
         audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
         audioOutputStream->mBlockedAudioTime = 0;
         audioOutputStream->mStream = AudioStream::AllocateStream();
         // XXX for now, allocate stereo output. But we need to fix this to
         // match the system's ideal channel configuration.
         audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
         audioOutputStream->mTrackID = tracks->GetID();
+
+        LogLatency(AsyncLatencyLogger::AudioStreamCreate,
+                   reinterpret_cast<uint64_t>(aStream),
+                   reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
       }
     }
   }
 
   for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
     if (!audioOutputStreamsFound[i]) {
       aStream->mAudioOutputStreams[i].mStream->Shutdown();
       aStream->mAudioOutputStreams.RemoveElementAt(i);
@@ -849,18 +853,19 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
         output.AppendNullData(endTicks - sliceEnd);
         NS_ASSERTION(endTicks == sliceEnd || track->IsEnded(),
                      "Ran out of data but track not ended?");
         output.ApplyVolume(volume);
         LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)",
                              aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
                              startTicks, endTicks));
       }
-      // XXX need unique id for stream & track
-      output.WriteTo((((uint64_t)i) << 32) | track->GetID(), audioOutput.mStream);
+      // Need unique id for stream & track - and we want it to match the inserter
+      output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
+                     audioOutput.mStream);
       t = end;
     }
   }
 }
 
 static void
 SetImageToBlackPixel(PlanarYCbCrImage* aImage)
 {
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -151,23 +151,27 @@ MediaEngineWebRTCAudioSource::Start(Sour
   {
     MonitorAutoLock lock(mMonitor);
     mSources.AppendElement(aStream);
   }
 
   AudioSegment* segment = new AudioSegment();
   aStream->AddTrack(aID, SAMPLE_FREQUENCY, 0, segment);
   aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
-  LOG(("Initial audio"));
-  mTrackID = aID;
+  LOG(("Start audio for stream %p", aStream));
 
   if (mState == kStarted) {
+    MOZ_ASSERT(aID == mTrackID);
     return NS_OK;
   }
   mState = kStarted;
+  mTrackID = aID;
+
+  // Make sure logger starts before capture
+  AsyncLatencyLogger::Get(true);
 
   // Configure audio processing in webrtc code
   Config(mEchoOn, webrtc::kEcUnchanged,
          mAgcOn, webrtc::kAgcUnchanged,
          mNoiseOn, webrtc::kNsUnchanged);
 
   if (mVoEBase->StartReceive(mChannel)) {
     return NS_ERROR_FAILURE;
@@ -365,21 +369,28 @@ MediaEngineWebRTCAudioSource::Process(in
 
     sample* dest = static_cast<sample*>(buffer->Data());
     memcpy(dest, audio10ms, length * sizeof(sample));
 
     AudioSegment segment;
     nsAutoTArray<const sample*,1> channels;
     channels.AppendElement(dest);
     segment.AppendFrames(buffer.forget(), channels, length);
+    TimeStamp insertTime;
+    segment.GetStartTime(insertTime);
 
     SourceMediaStream *source = mSources[i];
     if (source) {
       // This is safe from any thread, and is safe if the track is Finished
-      // or Destroyed
+      // or Destroyed.
+      // Make sure we include the stream and the track.
+      // The 0:1 is a flag to note when we've done the final insert for a given input block.
+      LogTime(AsyncLatencyLogger::AudioTrackInsertion, LATENCY_STREAM_ID(source, mTrackID),
+              (i+1 < len) ? 0 : 1, insertTime);
+
       source->AppendToTrack(mTrackID, &segment);
     }
   }
 
   return;
 }
 
 }
--- a/content/media/wmf/WMF.h
+++ b/content/media/wmf/WMF.h
@@ -26,16 +26,23 @@ which makes Windows Media Foundation una
 #include <mfobjects.h>
 #include <stdio.h>
 #include <mferror.h>
 #include <propvarutil.h>
 #include <wmcodecdsp.h>
 #include <initguid.h>
 #include <d3d9.h>
 #include <dxva2api.h>
+#include <wmcodecdsp.h>
+
+// Some SDK versions don't define the AAC decoder CLSID.
+#ifndef CLSID_CMSAACDecMFT
+extern const CLSID CLSID_CMSAACDecMFT;
+#define WMF_MUST_DEFINE_AAC_MFT_CLSID
+#endif
 
 namespace mozilla {
 namespace wmf {
 
 // Loads/Unloads all the DLLs in which the WMF functions are located.
 // The DLLs must be loaded before any of the WMF functions below will work.
 // All the function definitions below are wrappers which locate the
 // corresponding WMF function in the appropriate DLL (hence why LoadDLL()
@@ -99,16 +106,23 @@ HRESULT MFTEnumEx(GUID guidCategory,
 
 HRESULT MFGetService(IUnknown *punkObject,
                      REFGUID guidService,
                      REFIID riid,
                      LPVOID *ppvObject);
 
 HRESULT DXVA2CreateDirect3DDeviceManager9(UINT *pResetToken,
                                           IDirect3DDeviceManager9 **ppDXVAManager);
+
+HRESULT MFCreateSample(IMFSample **ppIMFSample);
+
+HRESULT MFCreateAlignedMemoryBuffer(DWORD cbMaxLength,
+                                    DWORD fAlignmentFlags,
+                                    IMFMediaBuffer **ppBuffer);
+
 } // end namespace wmf
 } // end namespace mozilla
 
 
 
 #pragma pop_macro("WINVER")
 
 #endif
--- a/content/media/wmf/WMFReader.cpp
+++ b/content/media/wmf/WMFReader.cpp
@@ -279,103 +279,16 @@ GetSourceReaderCanSeek(IMFSourceReader* 
   hr = wmf::PropVariantToUInt32(var, &flags);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
 
   return S_OK;
 }
 
-static HRESULT
-GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
-{
-  // Try to get the default stride from the media type.
-  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
-  if (SUCCEEDED(hr)) {
-    return S_OK;
-  }
-
-  // Stride attribute not set, calculate it.
-  GUID subtype = GUID_NULL;
-  uint32_t width = 0;
-  uint32_t height = 0;
-
-  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  return hr;
-}
-
-static int32_t
-MFOffsetToInt32(const MFOffset& aOffset)
-{
-  return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
-}
-
-// Gets the sub-region of the video frame that should be displayed.
-// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
-static HRESULT
-GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion)
-{
-  // Determine if "pan and scan" is enabled for this media. If it is, we
-  // only display a region of the video frame, not the entire frame.
-  BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
-
-  // If pan and scan mode is enabled. Try to get the display region.
-  HRESULT hr = E_FAIL;
-  MFVideoArea videoArea;
-  memset(&videoArea, 0, sizeof(MFVideoArea));
-  if (panScan) {
-    hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
-                             (UINT8*)&videoArea,
-                             sizeof(MFVideoArea),
-                             nullptr);
-  }
-
-  // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
-  // check for a minimimum display aperture.
-  if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
-    hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
-                             (UINT8*)&videoArea,
-                             sizeof(MFVideoArea),
-                             nullptr);
-  }
-
-  if (hr == MF_E_ATTRIBUTENOTFOUND) {
-    // Minimum display aperture is not set, for "backward compatibility with
-    // some components", check for a geometric aperture.
-    hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
-                             (UINT8*)&videoArea,
-                             sizeof(MFVideoArea),
-                             nullptr);
-  }
-
-  if (SUCCEEDED(hr)) {
-    // The media specified a picture region, return it.
-    aOutPictureRegion = nsIntRect(MFOffsetToInt32(videoArea.OffsetX),
-                                  MFOffsetToInt32(videoArea.OffsetY),
-                                  videoArea.Area.cx,
-                                  videoArea.Area.cy);
-    return S_OK;
-  }
-
-  // No picture region defined, fall back to using the entire video area.
-  UINT32 width = 0, height = 0;
-  hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-  aOutPictureRegion = nsIntRect(0, 0, width, height);
-  return S_OK;
-}
-
 HRESULT
 WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
 {
   NS_ENSURE_TRUE(aMediaType != nullptr, E_POINTER);
   HRESULT hr;
 
   // Verify that the video subtype is what we expect it to be.
   // When using hardware acceleration/DXVA2 the video format should
@@ -634,49 +547,16 @@ WMFReader::ReadMetadata(MediaInfo* aInfo
   *aInfo = mInfo;
   *aTags = nullptr;
   // aTags can be retrieved using techniques like used here:
   // http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx
 
   return NS_OK;
 }
 
-static int64_t
-GetSampleDuration(IMFSample* aSample)
-{
-  int64_t duration = 0;
-  aSample->GetSampleDuration(&duration);
-  return HNsToUsecs(duration);
-}
-
-HRESULT
-HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames)
-{
-  MOZ_ASSERT(aOutFrames);
-  const int64_t HNS_PER_S = USECS_PER_S * 10;
-  CheckedInt<int64_t> i = aHNs;
-  i *= aRate;
-  i /= HNS_PER_S;
-  NS_ENSURE_TRUE(i.isValid(), E_FAIL);
-  *aOutFrames = i.value();
-  return S_OK;
-}
-
-HRESULT
-FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs)
-{
-  MOZ_ASSERT(aOutUsecs);
-  CheckedInt<int64_t> i = aSamples;
-  i *= USECS_PER_S;
-  i /= aRate;
-  NS_ENSURE_TRUE(i.isValid(), E_FAIL);
-  *aOutUsecs = i.value();
-  return S_OK;
-}
-
 bool
 WMFReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   HRESULT hr;
   hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
                                  0, // control flags
--- a/content/media/wmf/WMFUtils.cpp
+++ b/content/media/wmf/WMFUtils.cpp
@@ -6,19 +6,27 @@
 
 #include "WMFUtils.h"
 #include <stdint.h>
 #include "mozilla/RefPtr.h"
 #include "prlog.h"
 #include "nsThreadUtils.h"
 #include "WinUtils.h"
 #include "nsWindowsHelpers.h"
+#include "mozilla/CheckedInt.h"
+#include "VideoUtils.h"
 
 using namespace mozilla::widget;
 
+#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
+// Some SDK versions don't define the AAC decoder CLSID.
+// {32D186A7-218F-4C75-8876-DD77273A8999}
+DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
+#endif
+
 namespace mozilla {
 
 struct GuidToName {
   GUID guid;
   const char* name;
 };
 
 #define GUID_TO_NAME_ENTRY(g) { g, #g }
@@ -208,23 +216,148 @@ DoGetInterface(IUnknown* aUnknown, void*
 {
   if (!aInterface)
     return E_POINTER;
   *aInterface = aUnknown;
   aUnknown->AddRef();
   return S_OK;
 }
 
-namespace wmf {
+HRESULT
+HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames)
+{
+  MOZ_ASSERT(aOutFrames);
+  const int64_t HNS_PER_S = USECS_PER_S * 10;
+  CheckedInt<int64_t> i = aHNs;
+  i *= aRate;
+  i /= HNS_PER_S;
+  NS_ENSURE_TRUE(i.isValid(), E_FAIL);
+  *aOutFrames = i.value();
+  return S_OK;
+}
+
+HRESULT
+FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs)
+{
+  MOZ_ASSERT(aOutUsecs);
+  CheckedInt<int64_t> i = aSamples;
+  i *= USECS_PER_S;
+  i /= aRate;
+  NS_ENSURE_TRUE(i.isValid(), E_FAIL);
+  *aOutUsecs = i.value();
+  return S_OK;
+}
+
+HRESULT
+GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
+{
+  // Try to get the default stride from the media type.
+  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
+  if (SUCCEEDED(hr)) {
+    return S_OK;
+  }
+
+  // Stride attribute not set, calculate it.
+  GUID subtype = GUID_NULL;
+  uint32_t width = 0;
+  uint32_t height = 0;
+
+  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  return hr;
+}
+
+int32_t
+MFOffsetToInt32(const MFOffset& aOffset)
+{
+  return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
+}
+
+int64_t
+GetSampleDuration(IMFSample* aSample)
+{
+  NS_ENSURE_TRUE(aSample, -1);
+  int64_t duration = 0;
+  aSample->GetSampleDuration(&duration);
+  return HNsToUsecs(duration);
+}
 
-// Some SDK versions don't define the AAC decoder CLSID.
-#ifndef CLSID_CMSAACDecMFT
-// {32D186A7-218F-4C75-8876-DD77273A8999}
-DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
-#endif
+int64_t
+GetSampleTime(IMFSample* aSample)
+{
+  NS_ENSURE_TRUE(aSample, -1);
+  LONGLONG timestampHns = 0;
+  HRESULT hr = aSample->GetSampleTime(&timestampHns);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), -1);
+  return HNsToUsecs(timestampHns);
+}
+
+// Gets the sub-region of the video frame that should be displayed.
+// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
+HRESULT
+GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion)
+{
+  // Determine if "pan and scan" is enabled for this media. If it is, we
+  // only display a region of the video frame, not the entire frame.
+  BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
+
+  // If pan and scan mode is enabled. Try to get the display region.
+  HRESULT hr = E_FAIL;
+  MFVideoArea videoArea;
+  memset(&videoArea, 0, sizeof(MFVideoArea));
+  if (panScan) {
+    hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
+                             (UINT8*)&videoArea,
+                             sizeof(MFVideoArea),
+                             nullptr);
+  }
+
+  // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
+  // check for a minimimum display aperture.
+  if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
+    hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
+                             (UINT8*)&videoArea,
+                             sizeof(MFVideoArea),
+                             nullptr);
+  }
+
+  if (hr == MF_E_ATTRIBUTENOTFOUND) {
+    // Minimum display aperture is not set, for "backward compatibility with
+    // some components", check for a geometric aperture.
+    hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
+                             (UINT8*)&videoArea,
+                             sizeof(MFVideoArea),
+                             nullptr);
+  }
+
+  if (SUCCEEDED(hr)) {
+    // The media specified a picture region, return it.
+    aOutPictureRegion = nsIntRect(MFOffsetToInt32(videoArea.OffsetX),
+                                  MFOffsetToInt32(videoArea.OffsetY),
+                                  videoArea.Area.cx,
+                                  videoArea.Area.cy);
+    return S_OK;
+  }
+
+  // No picture region defined, fall back to using the entire video area.
+  UINT32 width = 0, height = 0;
+  hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  aOutPictureRegion = nsIntRect(0, 0, width, height);
+  return S_OK;
+}
+
+namespace wmf {
 
 static bool
 IsSupportedDecoder(const GUID& aDecoderGUID)
 {
   return aDecoderGUID == CLSID_CMSH264DecoderMFT ||
          aDecoderGUID == CLSID_CMSAACDecMFT ||
          aDecoderGUID == CLSID_CMP3DecMediaObject;
 }
@@ -528,10 +661,28 @@ HRESULT
 DXVA2CreateDirect3DDeviceManager9(UINT *pResetToken,
                                   IDirect3DDeviceManager9 **ppDXVAManager)
 {
   DECL_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, UINT*, IDirect3DDeviceManager9 **);
   ENSURE_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, dxva2.dll)
   return (DXVA2CreateDirect3DDeviceManager9Ptr)(pResetToken, ppDXVAManager);
 }
 
+HRESULT
+MFCreateSample(IMFSample **ppIMFSample)
+{
+  DECL_FUNCTION_PTR(MFCreateSample, IMFSample **);
+  ENSURE_FUNCTION_PTR(MFCreateSample, mfplat.dll)
+  return (MFCreateSamplePtr)(ppIMFSample);
+}
+
+HRESULT
+MFCreateAlignedMemoryBuffer(DWORD cbMaxLength,
+                            DWORD fAlignmentFlags,
+                            IMFMediaBuffer **ppBuffer)
+{
+  DECL_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, DWORD, DWORD, IMFMediaBuffer**);
+  ENSURE_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, mfplat.dll)
+  return (MFCreateAlignedMemoryBufferPtr)(cbMaxLength, fAlignmentFlags, ppBuffer);
+}
+
 } // end namespace wmf
 } // end namespace mozilla
--- a/content/media/wmf/WMFUtils.h
+++ b/content/media/wmf/WMFUtils.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMF.h"
 #include "nsString.h"
+#include "nsRect.h"
+#include "VideoUtils.h"
 
 // Various utilities shared by WMF backend files.
 
 namespace mozilla {
 
 nsCString
 GetGUIDName(const GUID& guid);
 
@@ -59,9 +61,36 @@ HNsToUsecs(int64_t hNanoSecs) {
   return hNanoSecs / 10;
 }
 
 // Assigns aUnknown to *aInterface, and AddRef's it.
 // Helper for MSCOM QueryInterface implementations.
 HRESULT
 DoGetInterface(IUnknown* aUnknown, void** aInterface);
 
+HRESULT
+HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames);
+
+HRESULT
+FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs);
+
+HRESULT
+GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride);
+
+int32_t
+MFOffsetToInt32(const MFOffset& aOffset);
+
+// Gets the sub-region of the video frame that should be displayed.
+// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
+HRESULT
+GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion);
+
+// Returns the duration of a IMFSample in microseconds.
+// Returns -1 on failure.
+int64_t
+GetSampleDuration(IMFSample* aSample);
+
+// Returns the presentation time of a IMFSample in microseconds.
+// Returns -1 on failure.
+int64_t
+GetSampleTime(IMFSample* aSample);
+
 } // namespace mozilla
--- a/content/media/wmf/moz.build
+++ b/content/media/wmf/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'content'
 
 EXPORTS += [
     'WMF.h',
     'WMFDecoder.h',
     'WMFReader.h',
+    'WMFUtils.h',
 ]
 
 SOURCES += [
     'DXVA2Manager.cpp',
     'WMFByteStream.cpp',
     'WMFDecoder.cpp',
     'WMFReader.cpp',
     'WMFSourceReaderCallback.cpp',
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -340,17 +340,17 @@ nsXBLProtoImplField::InstallAccessors(JS
     return NS_OK;
 
   // FieldGetter and FieldSetter need to run in the XBL scope so that they can
   // see through any SOWs on their targets.
 
   // First, enter the XBL scope, and compile the functions there.
   JSAutoCompartment ac(aCx, scopeObject);
   JS::Rooted<JS::Value> wrappedClassObj(aCx, JS::ObjectValue(*aTargetClassObject));
-  if (!JS_WrapValue(aCx, wrappedClassObj.address()) || !JS_WrapId(aCx, id.address()))
+  if (!JS_WrapValue(aCx, &wrappedClassObj) || !JS_WrapId(aCx, id.address()))
     return NS_ERROR_OUT_OF_MEMORY;
 
   JS::Rooted<JSObject*> get(aCx,
     JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldGetter,
                                                          0, 0, scopeObject, id)));
   if (!get) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -440,19 +440,19 @@ nsXBLProtoImplField::InstallField(nsIScr
     return rv;
   }
 
 
   // Now, enter the node's compartment, wrap the eval result, and define it on
   // the bound node.
   JSAutoCompartment ac2(cx, aBoundNode);
   nsDependentString name(mName);
-  if (!JS_WrapValue(cx, result.address()) ||
+  if (!JS_WrapValue(cx, &result) ||
       !::JS_DefineUCProperty(cx, aBoundNode,
-                             reinterpret_cast<const jschar*>(mName), 
+                             reinterpret_cast<const jschar*>(mName),
                              name.Length(), result, nullptr, nullptr,
                              mJSAttributes)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   *aDidInstall = true;
   return NS_OK;
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -111,16 +111,17 @@
 #include "nsITabChild.h"
 #include "nsISiteSecurityService.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsIStructuredCloneContainer.h"
 #ifdef MOZ_PLACES
 #include "nsIFaviconService.h"
 #include "mozIAsyncFavicons.h"
 #endif
+#include "nsINetworkSeer.h"
 
 // Editor-related
 #include "nsIEditingSession.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsICachingChannel.h"
@@ -4582,25 +4583,19 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
         errorPageUrl.AppendASCII(escapedCSSClass.get());
     }
     errorPageUrl.AppendLiteral("&c=");
     errorPageUrl.AppendASCII(escapedCharset.get());
     errorPageUrl.AppendLiteral("&d=");
     errorPageUrl.AppendASCII(escapedDescription.get());
 
     // Append the manifest URL if the error comes from an app.
-    uint32_t appId;
-    nsresult rv = GetAppId(&appId);
-    if (appId != nsIScriptSecurityManager::NO_APP_ID &&
-        appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-      nsCOMPtr<nsIAppsService> appsService =
-        do_GetService(APPS_SERVICE_CONTRACTID);
-      NS_ASSERTION(appsService, "No AppsService available");
-      nsAutoString manifestURL;
-      appsService->GetManifestURLByLocalId(appId, manifestURL);
+    nsString manifestURL;
+    nsresult rv = GetAppManifestURL(manifestURL);
+    if (manifestURL.Length() > 0) {
       nsCString manifestParam;
       SAFE_ESCAPE(manifestParam,
                   NS_ConvertUTF16toUTF8(manifestURL).get(),
                   url_Path);
       errorPageUrl.AppendLiteral("&m=");
       errorPageUrl.AppendASCII(manifestParam.get());
     }
 
@@ -7014,19 +7009,22 @@ nsDocShell::EndPageLoad(nsIWebProgress *
         else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
             // Non-caching channels will simply return NS_ERROR_OFFLINE.
             // Caching channels would have to look at their flags to work
             // out which error to return. Or we can fix up the error here.
             if (!(mLoadType & LOAD_CMD_HISTORY))
                 aStatus = NS_ERROR_OFFLINE;
             DisplayLoadError(aStatus, url, nullptr, aChannel);
         }
-  } // if we have a host
-
-  return NS_OK;
+    } // if we have a host
+    else if (url && NS_SUCCEEDED(aStatus)) {
+        mozilla::net::SeerLearnRedirect(url, aChannel, this);
+    }
+
+    return NS_OK;
 }
 
 
 //*****************************************************************************
 // nsDocShell: Content Viewer Management
 //*****************************************************************************   
 
 NS_IMETHODIMP
@@ -9403,16 +9401,19 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     }
 
     nsAutoString srcdoc;
     if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC)
       srcdoc = aSrcdoc;
     else
       srcdoc = NullString();
 
+    mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD,
+                              this, nullptr);
+
     nsCOMPtr<nsIRequest> req;
     rv = DoURILoad(aURI, aReferrer,
                    !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                    owner, aTypeHint, aFileName, aPostData, aHeadersData,
                    aFirstParty, aDocShell, getter_AddRefs(req),
                    (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
@@ -12459,16 +12460,19 @@ nsDocShell::OnOverLink(nsIContent* aCont
   nsAutoCString spec;
   rv = aURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString uStr;
   rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);    
   NS_ENSURE_SUCCESS(rv, rv);
 
+  mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK,
+                            this, nullptr);
+
   if (browserChrome2) {
     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
     rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
                                               uStr, element);
   } else {
     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
   }
   return rv;
@@ -12727,16 +12731,35 @@ nsDocShell::GetAppId(uint32_t* aAppId)
         *aAppId = nsIScriptSecurityManager::NO_APP_ID;
         return NS_OK;
     }
 
     return parent->GetAppId(aAppId);
 }
 
 NS_IMETHODIMP
+nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
+{
+  uint32_t appId;
+  GetAppId(&appId);
+
+  if (appId != nsIScriptSecurityManager::NO_APP_ID &&
+      appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    nsCOMPtr<nsIAppsService> appsService =
+      do_GetService(APPS_SERVICE_CONTRACTID);
+    NS_ASSERTION(appsService, "No AppsService available");
+    appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
+  } else {
+    aAppManifestURL.SetLength(0);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
 {
     if (TabChild* tabChild = TabChild::GetFrom(this)) {
         *aOut = tabChild->IsAsyncPanZoomEnabled();
         return NS_OK;
     }
     *aOut = false;
     return NS_OK;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,17 +38,17 @@ interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(4c8cd9da-4e93-42ed-9901-3781e90458d9)]
+[scriptable, builtinclass, uuid(1470A132-99B2-44C3-B37D-D8093B2E29BF)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -738,16 +738,23 @@ interface nsIDocShell : nsIDocShellTreeI
    * return NO_APP_ID.  We never return UNKNOWN_APP_ID.
    *
    * Notice that a docshell may have an associated app even if it returns true
    * for isBrowserElement!
    */
   [infallible] readonly attribute unsigned long appId;
 
   /**
+   * Return the manifest URL of the app associated with this docshell.
+   *
+   * If there is no associated app in our hierarchy, we return empty string.
+   */
+  readonly attribute DOMString appManifestURL;
+
+  /**
    * Like nsIDocShellTreeItem::GetSameTypeParent, except this ignores <iframe
    * mozbrowser> and <iframe mozapp> boundaries.
    */
   nsIDocShell getSameTypeParentIgnoreBrowserAndAppBoundaries();
 
   /** 
    * True iff asynchronous panning and zooming is enabled for this
    * docshell.
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1586,17 +1586,17 @@ Navigator::DoNewResolve(JSContext* aCx, 
     rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val,
                                     getter_AddRefs(holder), true);
 
     if (NS_FAILED(rv)) {
       return Throw(aCx, rv);
     }
   }
 
-  if (!JS_WrapValue(aCx, prop_val.address())) {
+  if (!JS_WrapValue(aCx, &prop_val)) {
     return Throw(aCx, NS_ERROR_UNEXPECTED);
   }
 
   aValue.set(prop_val);
   return true;
 }
 
 static PLDHashOperator
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -2224,22 +2224,22 @@ BaseStubConstructor(nsIWeakReference* aW
         unsigned argc = args.length() + 1;
         JS::AutoValueVector argv(cx);
         argv.resize(argc);
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
         nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
         rv = WrapNative(cx, obj, currentWin, &NS_GET_IID(nsIDOMWindow),
                         true, argv.handleAt(0), getter_AddRefs(holder));
-        if (!JS_WrapValue(cx, &argv[0]))
+        if (!JS_WrapValue(cx, argv.handleAt(0)))
           return NS_ERROR_FAILURE;
 
         for (size_t i = 1; i < argc; ++i) {
           argv[i] = args[i - 1];
-          if (!JS_WrapValue(cx, &argv[i]))
+          if (!JS_WrapValue(cx, argv.handleAt(i)))
             return NS_ERROR_FAILURE;
         }
 
         JS::Rooted<JS::Value> frval(cx);
         bool ret = JS_CallFunctionValue(cx, thisObject, funval,
                                         argc, argv.begin(),
                                         frval.address());
 
@@ -2366,17 +2366,17 @@ public:
                        JS::Handle<JSObject*> obj, const jsval &val, bool *bp,
                        bool *_retval);
 
   nsresult Install(JSContext *cx, JS::Handle<JSObject*> target,
                    JS::Handle<JS::Value> aThisAsVal)
   {
     JS::Rooted<JS::Value> thisAsVal(cx, aThisAsVal);
     // The 'attrs' argument used to be JSPROP_PERMANENT. See bug 628612.
-    bool ok = JS_WrapValue(cx, thisAsVal.address()) &&
+    bool ok = JS_WrapValue(cx, &thisAsVal) &&
       ::JS_DefineUCProperty(cx, target,
                             reinterpret_cast<const jschar *>(mClassName),
                             NS_strlen(mClassName), thisAsVal, JS_PropertyStub,
                             JS_StrictPropertyStub, 0);
 
     return ok ? NS_OK : NS_ERROR_UNEXPECTED;
   }
 
@@ -2940,17 +2940,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec
     }
   }
 
   v = OBJECT_TO_JSVAL(dot_prototype);
 
   JSAutoCompartment ac(cx, class_obj);
 
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
-  if (!JS_WrapValue(cx, v.address()) ||
+  if (!JS_WrapValue(cx, &v) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_PERMANENT | JSPROP_READONLY)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *did_resolve = true;
 
@@ -3252,17 +3252,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       }
 
       rv = WrapNative(cx, scope, native, true, &prop_val,
                       getter_AddRefs(holder));
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (!JS_WrapValue(cx, prop_val.address())) {
+    if (!JS_WrapValue(cx, &prop_val)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     bool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
                                       JS_PropertyStub, JS_StrictPropertyStub,
                                       JSPROP_ENUMERATE);
 
     *did_resolve = true;
@@ -3563,17 +3563,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     JS::Rooted<JSObject*> scope(cx, wrapper->GetJSObject());
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     JS::Rooted<JS::Value> v(cx);
     rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true,
                     &v, getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    bool ok = JS_WrapValue(cx, v.address()) &&
+    bool ok = JS_WrapValue(cx, &v) &&
                 JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub,
                                       LocationSetterUnwrapper,
                                       JSPROP_PERMANENT | JSPROP_ENUMERATE);
 
     if (!ok) {
       return NS_ERROR_FAILURE;
     }
 
@@ -3687,17 +3687,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       // property
       *objp = obj;
 
       // NB: We need to do this for any Xray wrapper.
       if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
         // Unless our object is a native wrapper, in which case we have to
         // define it ourselves.
 
-        *_retval = JS_WrapValue(cx, v.address()) &&
+        *_retval = JS_WrapValue(cx, &v) &&
                    JS_DefineProperty(cx, obj, "document", v,
                                      JS_PropertyStub, JS_StrictPropertyStub,
                                      JSPROP_READONLY | JSPROP_ENUMERATE);
         if (!*_retval) {
           return NS_ERROR_UNEXPECTED;
         }
       }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -617,17 +617,18 @@ protected:
     return nsGlobalWindow::FromSupports(
       static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
   }
 
   // False return value means we threw an exception.  True return value
   // but false "found" means we didn't have a subframe at that index.
   bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id,
-                         JS::Value *vp, bool &found);
+                         JS::MutableHandle<JS::Value> vp,
+                         bool &found);
 
   // Returns a non-null window only if id is an index and we have a
   // window at that index.
   already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
                                                    JS::Handle<JSObject*> proxy,
                                                    JS::Handle<jsid> id);
 
   bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
@@ -703,17 +704,17 @@ nsOuterWindowProxy::getPropertyDescripto
 bool
 nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx,
                                              JS::Handle<JSObject*> proxy,
                                              JS::Handle<jsid> id,
                                              JS::MutableHandle<JSPropertyDescriptor> desc,
                                              unsigned flags)
 {
   bool found;
-  if (!GetSubframeWindow(cx, proxy, id, desc.value().address(), found)) {
+  if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) {
     return false;
   }
   if (found) {
     FillPropertyDescriptor(desc, proxy, true);
     return true;
   }
   // else fall through to js::Wrapper
 
@@ -824,17 +825,17 @@ nsOuterWindowProxy::get(JSContext *cx, J
 {
   if (id == nsDOMClassInfo::sWrappedJSObject_id &&
       xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
     vp.set(JS::ObjectValue(*proxy));
     return true;
   }
 
   bool found;
-  if (!GetSubframeWindow(cx, proxy, id, vp.address(), found)) {
+  if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
     return false;
   }
   if (found) {
     return true;
   }
   // Else fall through to js::Wrapper
 
   return js::Wrapper::get(cx, proxy, receiver, id, vp);
@@ -875,17 +876,18 @@ nsOuterWindowProxy::iterate(JSContext *c
   // BaseProxyHandler::iterate seems to do what we want here: fall
   // back on the property names returned from keys() and enumerate().
   return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
 }
 
 bool
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
-                                      JS::Handle<jsid> id, JS::Value* vp,
+                                      JS::Handle<jsid> id,
+                                      JS::MutableHandle<JS::Value> vp,
                                       bool& found)
 {
   nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id);
   if (!frame) {
     found = false;
     return true;
   }
 
@@ -895,17 +897,18 @@ nsOuterWindowProxy::GetSubframeWindow(JS
   global->EnsureInnerWindow();
   JSObject* obj = global->FastGetGlobalJSObject();
   // This null check fixes a hard-to-reproduce crash that occurs when we
   // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
   // comment 105.
   if (MOZ_UNLIKELY(!obj)) {
     return xpc::Throw(cx, NS_ERROR_FAILURE);
   }
-  *vp = JS::ObjectValue(*obj);
+
+  vp.setObject(*obj);
   return JS_WrapValue(cx, vp);
 }
 
 already_AddRefed<nsIDOMWindow>
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::Handle<jsid> id)
 {
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -126,17 +126,17 @@ nsHistory::GetState(JSContext* aCx, Erro
   if (variant) {
     JS::Rooted<JS::Value> jsData(aCx);
     aRv = variant->GetAsJSVal(jsData.address());
 
     if (aRv.Failed()) {
       return JS::UndefinedValue();
     }
 
-    if (!JS_WrapValue(aCx, jsData.address())) {
+    if (!JS_WrapValue(aCx, &jsData)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return JS::UndefinedValue();
     }
 
     return jsData;
   }
 
   return JS::UndefinedValue();
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1201,25 +1201,30 @@ nsJSContext::SetProperty(JS::Handle<JSOb
 
   Maybe<nsRootedJSValueArray> tempStorage;
 
   JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
   nsresult rv =
     ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  JS::AutoArrayRooter array(mContext, argc, argv);
+
   // got the arguments, now attach them.
 
   for (uint32_t i = 0; i < argc; ++i) {
-    if (!JS_WrapValue(mContext, &argv[i])) {
+    if (!JS_WrapValue(mContext, array.handleAt(i))) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
+  JSObject *args = ::JS_NewArrayObject(mContext, argc, array.array);
+  if (!args) {
+    return NS_ERROR_FAILURE;
+  }
   JS::Value vargs = OBJECT_TO_JSVAL(args);
 
   return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
     ? NS_OK
     : NS_ERROR_FAILURE;
 }
 
 nsresult
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -301,18 +301,23 @@ nsJSUtils::EvaluateString(JSContext* aCx
       if (aRetValue) {
         *aRetValue = exn;
       }
       JS_ClearPendingException(aCx);
     }
   }
 
   // Wrap the return value into whatever compartment aCx was in.
-  if (aRetValue && !JS_WrapValue(aCx, aRetValue))
-    return NS_ERROR_OUT_OF_MEMORY;
+  if (aRetValue) {
+    JS::Rooted<JS::Value> v(aCx, *aRetValue);
+    if (!JS_WrapValue(aCx, &v)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    *aRetValue = v;
+  }
   return rv;
 }
 
 //
 // nsDOMJSUtils.h
 //
 
 JSObject* GetDefaultScopeFromJSContext(JSContext *cx)
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -43,22 +43,23 @@ nsStructuredCloneContainer::InitFromJSVa
                                           JSContext* aCx)
 {
   NS_ENSURE_STATE(!mData);
   NS_ENSURE_ARG_POINTER(aCx);
 
   // Make sure that we serialize in the right context.
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   JS::Rooted<JS::Value> jsData(aCx, aData);
-  JS_WrapValue(aCx, jsData.address());
+  bool success = JS_WrapValue(aCx, &jsData);
+  NS_ENSURE_STATE(success);
 
   uint64_t* jsBytes = nullptr;
-  bool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
-                                         nullptr, nullptr,
-                                         JS::UndefinedHandleValue);
+  success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
+                                    nullptr, nullptr,
+                                    JS::UndefinedHandleValue);
   NS_ENSURE_STATE(success);
   NS_ENSURE_STATE(jsBytes);
 
   // Copy jsBytes into our own buffer.
   mData = (uint64_t*) malloc(mSize);
   if (!mData) {
     mSize = 0;
     mVersion = 0;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -176,20 +176,22 @@ ErrorResult::ThrowJSException(JSContext*
   }
 }
 
 void
 ErrorResult::ReportJSException(JSContext* cx)
 {
   MOZ_ASSERT(!mMightHaveUnreportedJSException,
              "Why didn't you tell us you planned to handle JS exceptions?");
-  if (JS_WrapValue(cx, &mJSException)) {
-    JS::RootedValue exception(cx, mJSException);
+
+  JS::Rooted<JS::Value> exception(cx, mJSException);
+  if (JS_WrapValue(cx, &exception)) {
     JS_SetPendingException(cx, exception);
   }
+  mJSException = exception;
   // If JS_WrapValue failed, not much we can do about it...  No matter
   // what, go ahead and unroot mJSException.
   JS_RemoveValueRoot(cx, &mJSException);
 }
 
 void
 ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
 {
@@ -1558,17 +1560,18 @@ ConcatJSString(JSContext* cx, const char
   }
 
   return JS_ConcatStrings(cx, preString, postString);
 }
 
 bool
 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
                JS::Handle<JSObject*> obj, const char* pre,
-               const char* post, JS::Value* v)
+               const char* post,
+               JS::MutableHandle<JS::Value> v)
 {
   JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
   toStringDesc.object().set(nullptr);
   toStringDesc.setAttributes(0);
   toStringDesc.setShortId(0);
   toStringDesc.setGetter(nullptr);
   toStringDesc.setSetter(nullptr);
   toStringDesc.value().set(JS::UndefinedValue());
@@ -1578,17 +1581,17 @@ NativeToString(JSContext* cx, JS::Handle
     return false;
   }
 
   JS::Rooted<JSString*> str(cx);
   {
     JSAutoCompartment ac(cx, obj);
     if (toStringDesc.object()) {
       JS::Rooted<JS::Value> toString(cx, toStringDesc.value());
-      if (!JS_WrapValue(cx, toString.address())) {
+      if (!JS_WrapValue(cx, &toString)) {
         return false;
       }
       MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject()));
       JS::Rooted<JS::Value> toStringResult(cx);
       if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr,
                                toStringResult.address())) {
         str = toStringResult.toString();
       } else {
@@ -1614,17 +1617,17 @@ NativeToString(JSContext* cx, JS::Handle
       str = ConcatJSString(cx, pre, str, post);
     }
   }
 
   if (!str) {
     return false;
   }
 
-  v->setString(str);
+  v.setString(str);
   return JS_WrapValue(cx, v);
 }
 
 // Dynamically ensure that two objects don't end up with the same reserved slot.
 class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard
 {
 public:
   AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -506,45 +506,45 @@ SetSystemOnlyWrapper(JSObject* obj, nsWr
 // needed.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isString());
   JSString* str = rval.toString();
   if (JS::GetGCThingZone(str) != js::GetContextZone(cx)) {
-    return JS_WrapValue(cx, rval.address());
+    return JS_WrapValue(cx, rval);
   }
   return true;
 }
 
 // Make sure to wrap the given object value into the right compartment as
 // needed.  This will work correctly, but possibly slowly, on all objects.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObject());
 
   JSObject* obj = &rval.toObject();
   if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
-    return JS_WrapValue(cx, rval.address());
+    return JS_WrapValue(cx, rval);
   }
 
   // We're same-compartment, but even then we might need to wrap
   // objects specially.  Check for that.
   if (GetSameCompartmentWrapperForDOMBinding(obj)) {
     // We're a new-binding object, and "obj" now points to the right thing
     rval.set(JS::ObjectValue(*obj));
     return true;
   }
 
   // It's not a WebIDL object.  But it might be an XPConnect one, in which case
   // we may need to outerize here, so make sure to call JS_WrapValue.
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Like MaybeWrapObjectValue, but also allows null
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObjectOrNull());
@@ -563,17 +563,17 @@ MaybeWrapNonDOMObjectValue(JSContext* cx
   MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
   MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags &
                JSCLASS_PRIVATE_IS_NSISUPPORTS));
 
   JSObject* obj = &rval.toObject();
   if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
     return true;
   }
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Like MaybeWrapNonDOMObjectValue but allows null
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObjectOrNull());
@@ -677,17 +677,17 @@ WrapNewBindingObject(JSContext* cx, JS::
   bool sameCompartment =
     js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
   if (sameCompartment && couldBeDOMBinding) {
     WrapNewBindingForSameCompartment(cx, obj, value, rval);
     return true;
   }
 
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext and a scope.
 template <class T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx,
@@ -718,17 +718,17 @@ WrapNewBindingNonWrapperCachedObject(JSC
 
   if (!obj) {
     return false;
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext, a scope, and a boolean outparam that
 // is true if the JSObject took ownership
 template <class T>
 inline bool
@@ -769,17 +769,17 @@ WrapNewBindingNonWrapperCachedOwnedObjec
 
   if (!obj) {
     return false;
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval.address());
+  return JS_WrapValue(cx, rval);
 }
 
 // Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
 template <template <typename> class SmartPtr, typename T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
                                      const SmartPtr<T>& value,
                                      JS::MutableHandle<JS::Value> rval)
@@ -1938,17 +1938,18 @@ void SetXrayExpandoChain(JSObject *obj, 
  *     interface or interface prototype object.
  * pre is a string that should be prefixed to the value.
  * post is a string that should be prefixed to the value.
  * v contains the JSString for the value if the function returns true.
  */
 bool
 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
                JS::Handle<JSObject*> obj, const char* pre,
-               const char* post, JS::Value* v);
+               const char* post,
+               JS::MutableHandle<JS::Value> v);
 
 HAS_MEMBER(JSBindingFinalized)
 
 template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value>
 struct JSBindingFinalized
 {
   static void Finalized(T* self)
   {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4600,19 +4600,19 @@ def wrapTypeIntoCurrentCompartment(type,
     """
     Take the thing named by "value" and if it contains "any",
     "object", or spidermonkey-interface types inside return a CGThing
     that will wrap them into the current compartment.
     """
     if type.isAny():
         assert not type.nullable()
         if isMember:
+            value = "JS::MutableHandleValue::fromMarkedLocation(&%s)" % value
+        else:
             value = "&" + value
-        else:
-            value = value + ".address()"
         return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
                          "  return false;\n"
                          "}" % value)
 
     if type.isObject():
         if isMember:
             value = "JS::MutableHandleObject::fromMarkedLocation(&%s)" % value
         else:
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -75,17 +75,17 @@ ThrowExceptionObject(JSContext* aCx, Exc
 
   // If we stored the original thrown JS value in the exception
   // (see XPCConvert::ConstructException) and we are in a web context
   // (i.e., not chrome), rethrow the original value. This only applies to JS
   // implemented components so we only need to check for this on the main
   // thread.
   if (NS_IsMainThread() && !IsCallerChrome() &&
       aException->StealJSVal(thrown.address())) {
-    if (!JS_WrapValue(aCx, thrown.address())) {
+    if (!JS_WrapValue(aCx, &thrown)) {
       return false;
     }
     JS_SetPendingException(aCx, thrown);
     return true;
   }
 
   JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
   if (!glob) {
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1,24 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
+#include "nsHashPropertyBag.h"
 #include "jsapi.h"
 #include "nsThread.h"
 #include "DeviceStorage.h"
 #include "mozilla/dom/CameraControlBinding.h"
-#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
+#include "nsIAppsService.h"
 #include "nsIObserverService.h"
 #include "nsIDOMDeviceStorage.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsXULAppAPI.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraCapabilities.h"
 #include "DOMCameraControl.h"
 #include "CameraCommon.h"
 #include "mozilla/dom/CameraManagerBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 
@@ -273,23 +276,26 @@ nsDOMCameraControl::StartRecording(JSCon
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  obs->NotifyObservers(nullptr,
+  nsRefPtr<nsHashPropertyBag> props = CreateRecordingDeviceEventsSubject();
+  obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
                        "recording-device-events",
                        NS_LITERAL_STRING("starting").get());
   // Forward recording events to parent process.
   // The events are gathered in chrome process and used for recording indicator
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting"));
+    unused << TabChild::GetFrom(mWindow)->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting"),
+                                                                    true /* isAudio */,
+                                                                    true /* isVideo */);
   }
 
   #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (mAudioChannelAgent) {
       // Camera app will stop recording when it falls to the background, so no callback is necessary.
       mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr);
@@ -313,23 +319,26 @@ void
 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     NS_WARNING("Could not get the Observer service for CameraControl::StopRecording.");
     aRv.Throw(NS_ERROR_FAILURE);
   }
 
-  obs->NotifyObservers(nullptr,
+  nsRefPtr<nsHashPropertyBag> props = CreateRecordingDeviceEventsSubject();
+  obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props) ,
                        "recording-device-events",
                        NS_LITERAL_STRING("shutdown").get());
   // Forward recording events to parent process.
   // The events are gathered in chrome process and used for recording indicator
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown"));
+    unused << TabChild::GetFrom(mWindow)->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown"),
+                                                                    true /* isAudio */,
+                                                                    true /* isVideo */);
   }
 
   #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
   }
   #endif
@@ -519,8 +528,44 @@ nsDOMCameraControl::Shutdown()
   mCameraControl->Shutdown();
 }
 
 nsRefPtr<ICameraControl>
 nsDOMCameraControl::GetNativeCameraControl()
 {
   return mCameraControl;
 }
+
+already_AddRefed<nsHashPropertyBag>
+nsDOMCameraControl::CreateRecordingDeviceEventsSubject()
+{
+  MOZ_ASSERT(mWindow);
+
+  nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+  props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), true);
+  props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), true);
+
+  nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
+  if (docShell) {
+    bool isApp;
+    DebugOnly<nsresult> rv = docShell->GetIsApp(&isApp);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    nsString requestURL;
+    if (isApp) {
+      rv = docShell->GetAppManifestURL(requestURL);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+    } else {
+      nsCString pageURL;
+      nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI();
+      MOZ_ASSERT(docURI);
+
+      rv = docURI->GetSpec(pageURL);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+      requestURL = NS_ConvertUTF8toUTF16(pageURL);
+    }
+    props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), isApp);
+    props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
+  }
+
+  return props.forget();
+}
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -10,16 +10,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "DictionaryHelpers.h"
 #include "ICameraControl.h"
 #include "DOMCameraPreview.h"
 #include "nsIDOMCameraManager.h"
 #include "CameraCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsProxyRelease.h"
+#include "nsHashPropertyBag.h"
 
 class nsDOMDeviceStorage;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 class CameraPictureOptions;
 template<typename T> class Optional;
@@ -94,16 +95,17 @@ public:
 protected:
   virtual ~nsDOMCameraControl();
 
 private:
   nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
   nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  already_AddRefed<nsHashPropertyBag> CreateRecordingDeviceEventsSubject();
 
 protected:
   /* additional members */
   nsRefPtr<ICameraControl>        mCameraControl; // non-DOM camera control
   nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent>  mAudioChannelAgent;
   nsCOMPtr<nsPIDOMWindow> mWindow;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1931,33 +1931,16 @@ ContentParent::RecvBroadcastVolume(const
     return true;
 #else
     NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
     return false;
 #endif
 }
 
 bool
-ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus)
-{
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    if (obs) {
-        // recording-device-ipc-events needs to gather more information from content process
-        nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
-        props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
-        obs->NotifyObservers((nsIPropertyBag2*) props,
-                             "recording-device-ipc-events",
-                             aRecordingStatus.get());
-    } else {
-        NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
-    }
-    return true;
-}
-
-bool
 ContentParent::SendNuwaFork()
 {
     if (this != sNuwaProcess) {
         return false;
     }
 
     CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod(
         this, &ContentParent::OnNuwaForkTimeout);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -159,16 +159,18 @@ public:
     /**
      * Kill our subprocess and make sure it dies.  Should only be used
      * in emergency situations since it bypasses the normal shutdown
      * process.
      */
     void KillHard();
 
     uint64_t ChildID() { return mChildID; }
+    const nsString& AppManifestURL() const { return mAppManifestURL; }
+
     bool IsPreallocated();
 
     /**
      * Get a user-friendly name for this ContentParent.  We make no guarantees
      * about this name: It might not be unique, apps can spoof special names,
      * etc.  So please don't use this name to make any decisions about the
      * ContentParent based on the value returned here.
      */
@@ -466,18 +468,16 @@ private:
 
     virtual bool RecvAudioChannelChangedNotification();
 
     virtual bool RecvAudioChannelChangeDefVolChannel(
       const AudioChannelType& aType, const bool& aHidden);
 
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
 
-    virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus);
-
     virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
 
     virtual bool RecvNuwaReady() MOZ_OVERRIDE;
 
     virtual bool RecvAddNewProcess(const uint32_t& aPid,
                                    const InfallibleTArray<ProtocolFdMapping>& aFds) MOZ_OVERRIDE;
 
     virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) MOZ_OVERRIDE;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -299,16 +299,24 @@ parent:
 
     /**
      * Notifies the parent about a scroll event. The pres shell ID and
      * view ID identify which scrollable (sub-)frame was scrolled, and
      * the new scroll offset for that frame is sent.
      */
     UpdateScrollOffset(uint32_t aPresShellId, ViewID aViewId, CSSIntPoint aScrollOffset);
 
+    /**
+     * Notifies the parent about a recording device is starting or shutdown.
+     * @param recordingStatus starting or shutdown
+     * @param isAudio recording start with microphone
+     * @param isVideo recording start with camera
+     */
+    async RecordingDeviceEvents(nsString recordingStatus, bool isAudio, bool isVideo);
+
     __delete__();
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -460,18 +460,16 @@ parent:
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
     // get nsIVolumeService to broadcast volume information
     async BroadcastVolume(nsString volumeName);
 
-    async RecordingDeviceEvents(nsString recordingStatus);
-
     // Notify the parent that the child has finished handling a system message.
     async SystemMessageHandled();
 
     NuwaReady();
 
     sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
 
     // called by the child (test code only) to propagate volume changes to the parent
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/unused.h"
 #include "nsCOMPtr.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsEventStateManager.h"
 #include "nsFocusManager.h"
 #include "nsFrameLoader.h"
+#include "nsHashPropertyBag.h"
 #include "nsIContent.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsIDialogCreator.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -1620,10 +1621,44 @@ bool
 TabParent::RecvContentReceivedTouch(const bool& aPreventDefault)
 {
   if (RenderFrameParent* rfp = GetRenderFrame()) {
     rfp->ContentReceivedTouch(aPreventDefault);
   }
   return true;
 }
 
+bool
+TabParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
+                                     const bool& aIsAudio,
+                                     const bool& aIsVideo)
+{
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+        // recording-device-ipc-events needs to gather more information from content process
+        nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+        props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), Manager()->ChildID());
+        props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), Manager()->IsForApp());
+        props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio);
+        props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo);
+
+        nsString requestURL;
+        if (Manager()->IsForApp()) {
+          requestURL = Manager()->AppManifestURL();
+        } else {
+          nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+          NS_ENSURE_TRUE(frameLoader, true);
+
+          frameLoader->GetURL(requestURL);
+        }
+        props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
+
+        obs->NotifyObservers((nsIPropertyBag2*) props,
+                             "recording-device-ipc-events",
+                             aRecordingStatus.get());
+    } else {
+        NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
+    }
+    return true;
+}
+
 } // namespace tabs
 } // namespace mozilla
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -157,16 +157,19 @@ public:
     virtual bool RecvGetDefaultScale(double* aValue);
     virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
     virtual bool RecvZoomToRect(const CSSRect& aRect);
     virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom,
                                            const CSSToScreenScale& aMinZoom,
                                            const CSSToScreenScale& aMaxZoom);
     virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset);
     virtual bool RecvContentReceivedTouch(const bool& aPreventDefault);
+    virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
+                                           const bool& aIsAudio,
+                                           const bool& aIsVideo);
     virtual PContentDialogParent* AllocPContentDialogParent(const uint32_t& aType,
                                                             const nsCString& aName,
                                                             const nsCString& aFeatures,
                                                             const InfallibleTArray<int>& aIntParams,
                                                             const InfallibleTArray<nsString>& aStringParams);
     virtual bool DeallocPContentDialogParent(PContentDialogParent* aDialog)
     {
       delete aDialog;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1,33 +1,38 @@
 /* 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 "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "GetUserMediaRequest.h"
+#include "nsHashPropertyBag.h"
 #ifdef MOZ_WIDGET_GONK
 #include "nsIAudioManager.h"
 #endif
+#include "nsIAppsService.h"
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
-#include "mozilla/dom/ContentChild.h"
+#include "nsIScriptSecurityManager.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 
+#include "Latency.h"
+
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
 #include "mozilla/Preferences.h"
@@ -133,16 +138,55 @@ static nsresult ValidateTrackConstraints
     nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
                                       aNormalized.mMandatory,
                                       aOutUnknownConstraint);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
+static already_AddRefed<nsHashPropertyBag>
+CreateRecordingDeviceEventsSubject(nsPIDOMWindow* aWindow,
+                                   const bool aIsAudio,
+                                   const bool aIsVideo)
+{
+  MOZ_ASSERT(aWindow);
+
+  nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+  props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio);
+  props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo);
+
+  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
+  if (docShell) {
+    bool isApp;
+    DebugOnly<nsresult> rv = docShell->GetIsApp(&isApp);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    nsString requestURL;
+    if (isApp) {
+      rv = docShell->GetAppManifestURL(requestURL);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+    } else {
+      nsCString pageURL;
+      nsCOMPtr<nsIURI> docURI = aWindow->GetDocumentURI();
+      MOZ_ASSERT(docURI);
+
+      rv = docURI->GetSpec(pageURL);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+      requestURL = NS_ConvertUTF8toUTF16(pageURL);
+    }
+
+    props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
+    props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), isApp);
+  }
+
+  return props.forget();
+}
+
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread. The success callback is also passed here
  * so it can be released correctly.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
 public:
@@ -559,16 +603,22 @@ public:
     nsRefPtr<SourceMediaStream> stream = gm->CreateSourceStream(nullptr);
 
     // connect the source stream to the track union stream to avoid us blocking
     trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
     nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()->
       AllocateInputPort(stream, MediaInputPort::FLAG_BLOCK_OUTPUT);
     trackunion->mSourceStream = stream;
     trackunion->mPort = port.forget();
+    // Log the relationship between SourceMediaStream and TrackUnion stream
+    // Make sure logger starts before capture
+    AsyncLatencyLogger::Get(true);
+    LogLatency(AsyncLatencyLogger::MediaStreamCreate,
+               reinterpret_cast<uint64_t>(stream.get()),
+               reinterpret_cast<int64_t>(trackunion->GetStream()));
 
     trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
 
     // The listener was added at the begining in an inactive state.
     // Activate our listener. We'll call Start() on the source when get a callback
     // that the MediaStream has started consuming. The listener is freed
     // when the page is invalidated (on navigation or close).
     mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
@@ -579,17 +629,17 @@ public:
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationRunnable
     // to ensure it's kept alive until the MediaOperationRunnable runs (at least).
     nsIThread *mediaThread = MediaManager::GetThread();
     nsRefPtr<MediaOperationRunnable> runnable(
       new MediaOperationRunnable(MEDIA_START, mListener, trackunion,
                                  tracksAvailableCallback,
-                                 mAudioSource, mVideoSource, false));
+                                 mAudioSource, mVideoSource, false, mWindowID));
     mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
 #ifdef MOZ_WEBRTC
     // Right now these configs are only of use if webrtc is available
     nsresult rv;
     nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
@@ -1725,17 +1775,17 @@ GetUserMediaCallbackMediaStreamListener:
   nsRefPtr<MediaOperationRunnable> runnable;
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   runnable = new MediaOperationRunnable(MEDIA_STOP,
                                         this, nullptr, nullptr,
                                         mAudioSource, mVideoSource,
-                                        mFinished);
+                                        mFinished, mWindowID);
   mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
@@ -1782,20 +1832,27 @@ GetUserMediaNotificationEvent::Run()
     break;
   case STOPPING:
     msg = NS_LITERAL_STRING("shutdown");
     if (mListener) {
       mListener->SetStopped();
     }
     break;
   }
-  obs->NotifyObservers(nullptr,
+
+  nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
+  MOZ_ASSERT(window);
+
+  nsRefPtr<nsHashPropertyBag> props = 
+    CreateRecordingDeviceEventsSubject(window, mIsAudio, mIsVideo);
+
+  obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
 		       "recording-device-events",
 		       msg.get());
   // Forward recording events to parent process.
   // The events are gathered in chrome process and used for recording indicator
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    unused << dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(msg);
+    unused << dom::TabChild::GetFrom(window)->SendRecordingDeviceEvents(msg, mIsAudio, mIsVideo);
   }
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -199,36 +199,42 @@ private:
 class GetUserMediaNotificationEvent: public nsRunnable
 {
   public:
     enum GetUserMediaStatus {
       STARTING,
       STOPPING
     };
     GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
-                                  GetUserMediaStatus aStatus)
-    : mListener(aListener), mStatus(aStatus) {}
+                                  GetUserMediaStatus aStatus,
+                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
+    : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
+    , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
                                   already_AddRefed<DOMMediaStream> aStream,
-                                  DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback)
+                                  DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
+                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
     : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
-      mStatus(aStatus) {}
+      mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID) {}
     virtual ~GetUserMediaNotificationEvent()
     {
 
     }
 
     NS_IMETHOD Run() MOZ_OVERRIDE;
 
   protected:
     nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
     nsRefPtr<DOMMediaStream> mStream;
     nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
     GetUserMediaStatus mStatus;
+    bool mIsAudio;
+    bool mIsVideo;
+    uint64_t mWindowID;
 };
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP
 } MediaOperation;
 
 // Generic class for running long media operations like Start off the main
@@ -239,24 +245,26 @@ class MediaOperationRunnable : public ns
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
-    bool aNeedsFinish)
+    bool aNeedsFinish,
+    uint64_t aWindowID)
     : mType(aType)
     , mStream(aStream)
     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
     , mFinish(aNeedsFinish)
+    , mWindowID(aWindowID)
     {}
 
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
 
   NS_IMETHOD
@@ -298,17 +306,20 @@ public:
 
           MM_LOG(("started all sources"));
           // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
           // because mOnTracksAvailableCallback needs to be added to mStream
           // on the main thread.
           nsRefPtr<GetUserMediaNotificationEvent> event =
             new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
                                               mStream.forget(),
-                                              mOnTracksAvailableCallback.forget());
+                                              mOnTracksAvailableCallback.forget(),
+                                              mAudioSource != nullptr,
+                                              mVideoSource != nullptr,
+                                              mWindowID);
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
 
       case MEDIA_STOP:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           if (mAudioSource) {
@@ -319,17 +330,21 @@ public:
             mVideoSource->Stop(source, kVideoTrack);
             mVideoSource->Deallocate();
           }
           // Do this after stopping all tracks with EndTrack()
           if (mFinish) {
             source->Finish();
           }
           nsRefPtr<GetUserMediaNotificationEvent> event =
-            new GetUserMediaNotificationEvent(mListener, GetUserMediaNotificationEvent::STOPPING);
+            new GetUserMediaNotificationEvent(mListener,
+                                              GetUserMediaNotificationEvent::STOPPING,
+                                              mAudioSource != nullptr,
+                                              mVideoSource != nullptr,
+                                              mWindowID);
 
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
 
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
@@ -340,16 +355,17 @@ public:
 private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   bool mFinish;
+  uint64_t mWindowID;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -324,17 +324,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
         // lose the error), or it might be JS that then proceeds to
         // cause an error of its own (which will also make us lose
         // this error).
         ::JS_ReportPendingException(cx);
     }
 
     // If we took the sandbox path above, v might be in the sandbox
     // compartment.
-    if (!JS_WrapValue(cx, v.address())) {
+    if (!JS_WrapValue(cx, &v)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
         return NS_ERROR_MALFORMED_URI;
     } else if (v.isUndefined()) {
         return NS_ERROR_DOM_RETVAL_UNDEFINED;
     } else {
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/file_bug927901.html
@@ -0,0 +1,18 @@
+<html>
+  <head>
+    <title></title>
+    <script>
+      var ret = "pass";
+      try {
+        window.foo = window.crypto.getRandomValues;
+      } catch(ex) {
+        ret = "" + ex;
+      }
+      parent.postMessage(ret, "*");
+    </script>
+    <style>
+    </style>
+  </head>
+  <body onload="document.body.textContent = 'Crypto test file on ' + location">
+  </body>
+</html>
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -27,16 +27,17 @@ support-files =
   iframe_bug430276-2.html
   iframe_bug430276.html
   iframe_bug440572.html
   iframe_bug49312.html
   iframe_domparser_after_blank.html
   utils_bug260264.js
   utils_bug743615.js
   worker_bug743615.js
+  file_bug927901.html
 
 [test_DOMWindowCreated_chromeonly.html]
 [test_bug132255.html]
 [test_bug159849.html]
 [test_bug260264.html]
 [test_bug260264_nested.html]
 [test_bug265203.html]
 [test_bug291377.html]
@@ -123,16 +124,17 @@ support-files =
 [test_bug809290.html]
 [test_bug817476.html]
 [test_bug823173.html]
 [test_bug848088.html]
 [test_bug850517.html]
 [test_bug857555.html]
 [test_bug862540.html]
 [test_bug876098.html]
+[test_bug927901.html]
 [test_devicemotion_multiple_listeners.html]
 [test_domparser_after_blank.html]
 [test_onerror_message.html]
 [test_protochains.html]
 [test_resize_move_windows.html]
 [test_sizetocontent_clamp.html]
 [test_toJSON.html]
 [test_window_bar.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug927901.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=927901
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 927901</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 927901 **/
+  SimpleTest.waitForExplicitFinish();
+
+  var counter = 0;
+  window.onmessage = function(e) {
+    ++counter;
+    is(e.data, "pass", "Accessing window.crypto.getRandomValues in the iframe should have succeeded!");
+    if (counter == 1) {
+      document.getElementById("testiframe").src =
+        "http://mochi.test:8888/tests/dom/tests/mochitest/bugs/file_bug927901.html "
+    } else if (counter == 2) {
+      SimpleTest.finish();
+    }
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=927901">Mozilla Bug 927901</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="testiframe" src="http://test1.example.org:8000/tests/dom/tests/mochitest/bugs/file_bug927901.html"></iframe>
+</body>
+</html>
--- a/dom/tests/mochitest/general/test_offsets.html
+++ b/dom/tests/mochitest/general/test_offsets.html
@@ -11,51 +11,51 @@
     -moz-box-sizing: content-box;
   }
 </style>
 </head>
 <body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);"
       style="margin: 1px; border: 2px solid black; padding: 4px;">
 
 <div id="testelements" style="margin: 0; border: 0; padding: 0;">
-  <input id="input1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
+  <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
          _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
-         _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
+         _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div>
   <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;"
        _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
     <div id="inner">Inner Text</div>
   </div>
 
   <div id="absolute" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0;">
     <div id="absolute-block" _offsetParent="absolute">
-      <input id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="absolutelr" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0; left: 90px; top: 130px;">
     This is some absolute positioned text.
     <div id="absolutelr-block" _offsetParent="absolutelr">
-      <input id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="relative" style="position: relative; margin: 2px; border: 1px solid orange; padding: 7px; left: 10px; top: 5px;">
     This is some relative positioned text.
     <div id="relative-block" _offsetParent="relative">
-      <input id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;">
     This is some fixed positioned text.
     <div id="fixed-block" _offsetParent="fixed">
-      <input id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;">
+      <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="scrollbox"
        style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p>
     <p id="p2">Two</p>
@@ -65,23 +65,23 @@
                                value="This button is much longer than the others">
   </p></div>
 
   <div id="overflow-visible" style="width:100px; height:100px;">
     <div id="overflow-visible-1" style="width:200px; height:1px; background:yellow;"></div>
     <div id="overflow-visible-2" style="height:200px; background:lime;"></div>
   </div>
 
-  <input id="input-displaynone" style="display: none; border: 0; padding: 0;"
-         _offsetParent="null">
+  <div id="div-displaynone" style="display: none; border: 0; padding: 0;"
+         _offsetParent="null"></div>
   <p id="p3" style="margin: 2px; border: 0; padding: 1px;"
          _offsetLeft="9" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
          _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32">
-    <input id="input-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;">
+    <div id="div-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;"></div>
   </p>
 
 </div>
 
 <div id="scrollbox-test" style="float: left; overflow: scroll; margin: 0; border: 0; padding: 0"></div>
 
 <script type="application/javascript">
 SimpleTest.waitForExplicitFinish();
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -105,17 +105,17 @@ function testElement(element)
   isEqualAppunits(boundingrect.height, borderTop + paddingTop + height + paddingBottom + borderBottom,
      element.id + " bounding rect height");
   isEqualAppunits(boundingrect.right - boundingrect.left, boundingrect.width,
      element.id + " bounding rect right");
   isEqualAppunits(boundingrect.bottom - boundingrect.top, boundingrect.height,
      element.id + " bounding rect bottom");
 
   var rects = element.getClientRects();
-  if (element.id == "input-displaynone" || element.id == "nonappended") {
+  if (element.id == "div-displaynone" || element.id == "nonappended") {
     is(rects.length, 0, element.id + " getClientRects empty");
   }
   else {
     is(rects[0].left, boundingrect.left, element.id + " getClientRects left");
     is(rects[0].top, boundingrect.top, element.id + " getClientRects top");
     is(rects[0].right, boundingrect.right, element.id + " getClientRects right");
     is(rects[0].bottom, boundingrect.bottom, element.id + " getClientRects bottom");
   }
@@ -189,22 +189,19 @@ function checkCoords(element, type, left
   checkCoord(element, type + "Height", height, testname);
 
   if (element instanceof SVGElement)
     return;
 
   if (element.id == "outerpopup" && !element.parentNode.open) // closed popup
     return;
 
-  if (element.id == "input-displaynone" || element.id == "nonappended") // hidden elements
+  if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements
     ok(element[type + "Width"] == 0 && element[type + "Height"] == 0,
        element.id + " has zero " + type + " width and height");
-  else if (element.id != "input-nosize") // for some reason, this element has a width of 2
-    ok(element[type + "Width"] > 0 && element[type + "Height"] > 0,
-       element.id + " has non-zero " + type + " width and height");
 }
 
 function gcs(element, prop)
 {
   var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ?
                    element.getAttribute(prop) : getComputedStyle(element, "")[prop];
   if (propVal == "auto")
     return 0;
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -24,16 +24,17 @@
 #include "nsICachingChannel.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIFileURL.h"
 #include "nsCRT.h"
 #include "nsIDocument.h"
+#include "nsINetworkSeer.h"
 
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 
 #include "nsIMemoryReporter.h"
 #include "Image.h"
 #include "DiscardTracker.h"
 
@@ -1233,16 +1234,19 @@ bool imgLoader::ValidateRequestWithNewCh
     // In the mean time, we must defer notifications because we are added to
     // the imgRequest's proxy list, and we can get extra notifications
     // resulting from methods such as RequestDecode(). See bug 579122.
     proxy->SetNotificationsDeferred(true);
 
     // Add the proxy without notifying
     hvc->AddProxy(proxy);
 
+    mozilla::net::SeerLearn(aURI, aInitialDocumentURI,
+        nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
+
     rv = newChannel->AsyncOpen(listener, nullptr);
     if (NS_SUCCEEDED(rv))
       NS_ADDREF(*aProxyRequest = req.get());
 
     return NS_SUCCEEDED(rv);
   }
 }
 
@@ -1730,16 +1734,19 @@ nsresult imgLoader::LoadImage(nsIURI *aU
       }
 
       listener = corsproxy;
     }
 
     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
 
+    mozilla::net::SeerLearn(aURI, aInitialDocumentURI,
+        nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
+
     nsresult openRes = newChannel->AsyncOpen(listener, nullptr);
 
     if (NS_FAILED(openRes)) {
       PR_LOG(GetImgLog(), PR_LOG_DEBUG,
              ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
               this, openRes));
       request->CancelAndAbort(openRes);
       return openRes;
--- a/js/jsd/jsd_val.cpp
+++ b/js/jsd/jsd_val.cpp
@@ -191,17 +191,17 @@ jsd_GetValueString(JSDContext* jsdc, JSD
         AutoSaveExceptionState as(cx);
         string = JS_ValueToString(cx, jsdval->val);
     }
 
     JSAutoCompartment ac2(cx, jsdc->glob);
     if(string) {
         stringval = STRING_TO_JSVAL(string);
     }
-    if(!string || !JS_WrapValue(cx, stringval.address())) {
+    if(!string || !JS_WrapValue(cx, &stringval)) {
         return nullptr;
     }
 
     jsdval->string = JSVAL_TO_STRING(stringval);
     if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
         jsdval->string = nullptr;
 
     return jsdval->string;
@@ -248,17 +248,17 @@ jsd_NewValue(JSDContext* jsdc, jsval val
 
     if(JSVAL_IS_GCTHING(val))
     {
         bool ok;
         JSAutoCompartment ac(cx, jsdc->glob);
 
         ok = JS_AddNamedValueRoot(cx, &jsdval->val, "JSDValue");
         if(ok && JSVAL_IS_STRING(val)) {
-            if(!JS_WrapValue(cx, val.address())) {
+            if(!JS_WrapValue(cx, &val)) {
                 ok = false;
             }
         }
 
         if(!ok)
         {
             free(jsdval);
             return nullptr;
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -2315,21 +2315,22 @@ jsdValue::Refresh()
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdValue::GetWrappedValue(JSContext* aCx, JS::Value* aRetval)
 {
     ASSERT_VALID_EPHEMERAL;
 
-    *aRetval = JSD_GetValueWrappedJSVal(mCx, mValue);
-    if (!JS_WrapValue(aCx, aRetval)) {
+    JS::RootedValue value(aCx, JSD_GetValueWrappedJSVal(mCx, mValue));
+    if (!JS_WrapValue(aCx, &value)) {
         return NS_ERROR_FAILURE;
     }
 
+    *aRetval = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdValue::GetScript(jsdIScript **_rval)
 {
     ASSERT_VALID_EPHEMERAL;
     JSDScript *script = JSD_GetScriptForValue(mCx, mValue);
--- a/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
+++ b/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
@@ -1,8 +1,9 @@
+// |jit-test| slow
 // This test is too slow to run with ASan in a debug configuration
 if (getBuildConfiguration()['asan'] && getBuildConfiguration()['debug']) quit(0);
 
 function fatty() {
     try {
         fatty();
     } catch (e) {
         foo();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/BaselineIC.h"
 
+#include "mozilla/DebugOnly.h"
 #include "mozilla/TemplateLib.h"
 
 #include "jsautooplen.h"
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineHelpers.h"
@@ -26,16 +27,18 @@
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/IonFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/StringObject-inl.h"
 
+using mozilla::DebugOnly;
+
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
 void
 FallbackICSpew(JSContext *cx, ICFallbackStub *stub, const char *fmt, ...)
 {
     if (IonSpewEnabled(IonSpew_BaselineICFallback)) {
@@ -4133,18 +4136,18 @@ ICGetElemNativeCompiler::generateStubCod
         JS_ASSERT(R0 == JSReturnOperand);
         leaveStubFrame(masm);
         masm.moveValue(JSReturnOperand, R1);
 
         // Unstow R0
         EmitUnstowICValues(masm, 1);
 
         // Extract string from R1 again.
-        Register strExtract2 = masm.extractString(R1, ExtractTemp1);
-        JS_ASSERT(strExtract2 == strExtract);
+        DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1);
+        JS_ASSERT(Register(strExtract2) == strExtract);
 
         masm.bind(&skipAtomize);
     }
 
     // Since this stub sometimes enter a stub frame, we manually set this to true (lie).
     entersStubFrame_ = true;
 
     // Key has been atomized if necessary.  Do identity check on string pointer.
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2126,17 +2126,17 @@ jit::SetEnterJitData(JSContext *cx, Ente
                 data.calleeToken = CalleeToToken(iter.callee());
         }
     }
 
     return true;
 }
 
 IonExecStatus
-jit::Cannon(JSContext *cx, RunState &state)
+jit::IonCannon(JSContext *cx, RunState &state)
 {
     IonScript *ion = state.script()->ionScript();
 
     EnterJitData data(cx);
     data.jitcode = ion->method()->raw();
 
     AutoValueVector vals(cx);
     if (!SetEnterJitData(cx, data, state, vals))
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -333,17 +333,17 @@ IsErrorStatus(IonExecStatus status)
 {
     return status == IonExec_Error || status == IonExec_Aborted;
 }
 
 struct EnterJitData;
 
 bool SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals);
 
-IonExecStatus Cannon(JSContext *cx, RunState &state);
+IonExecStatus IonCannon(JSContext *cx, RunState &state);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -275,17 +275,17 @@ IonBuilder::canInlineTarget(JSFunction *
 
     if (target->getParent() != &script()->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
         return false;
     }
 
     // Allow constructing lazy scripts when performing the definite properties
     // analysis, as baseline has not been used to warm the caller up yet.
-    if (target->isInterpretedLazy() && info().executionMode() == DefinitePropertiesAnalysis) {
+    if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) {
         if (!target->getOrCreateScript(context()))
             return false;
 
         RootedScript script(context(), target->nonLazyScript());
         if (!script->hasBaselineScript()) {
             MethodStatus status = BaselineCompile(context(), script);
             if (status != Method_Compiled)
                 return false;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1727,27 +1727,20 @@ LIRGenerator::visitToInt32(MToInt32 *con
 
       case MIRType_Double:
       {
         LDoubleToInt32 *lir = new LDoubleToInt32(useRegister(opd));
         return assignSnapshot(lir) && define(lir, convert);
       }
 
       case MIRType_String:
-        // Strings are complicated - we don't handle them yet.
-        IonSpew(IonSpew_Abort, "String to Int32 not supported yet.");
-        return false;
-
       case MIRType_Object:
-        // Objects might be effectful.
-        IonSpew(IonSpew_Abort, "Object to Int32 not supported yet.");
-        return false;
-
       case MIRType_Undefined:
-        IonSpew(IonSpew_Abort, "Undefined coerces to NaN, not int32_t.");
+        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        MOZ_ASSUME_UNREACHABLE("ToInt32 invalid input type");
         return false;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2914,17 +2914,19 @@ class MAsmJSUnsignedToFloat32
     }
 
     bool canProduceFloat32() const { return true; }
 };
 
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
-class MToInt32 : public MUnaryInstruction
+class MToInt32
+  : public MUnaryInstruction,
+    public ToInt32Policy
 {
     bool canBeNegativeZero_;
 
     MToInt32(MDefinition *def)
       : MUnaryInstruction(def),
         canBeNegativeZero_(true)
     {
         setResultType(MIRType_Int32);
@@ -2945,16 +2947,20 @@ class MToInt32 : public MUnaryInstructio
 
     bool canBeNegativeZero() {
         return canBeNegativeZero_;
     }
     void setCanBeNegativeZero(bool negativeZero) {
         canBeNegativeZero_ = negativeZero;
     }
 
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     void computeRange();
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -531,16 +531,37 @@ ToDoublePolicy::staticAdjustInputs(MInst
     if (in->type() != MIRType_Object && in->type() != MIRType_String)
         return true;
 
     in = boxAt(ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
 
+bool
+ToInt32Policy::staticAdjustInputs(MInstruction *ins)
+{
+    JS_ASSERT(ins->isToInt32());
+
+    MDefinition *in = ins->getOperand(0);
+    switch (in->type()) {
+      case MIRType_Object:
+      case MIRType_String:
+      case MIRType_Undefined:
+        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        in = boxAt(ins, in);
+        ins->replaceOperand(0, in);
+        break;
+      default:
+        break;
+    }
+
+    return true;
+}
+
 template <unsigned Op>
 bool
 ObjectPolicy<Op>::staticAdjustInputs(MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Object || in->type() == MIRType_Slots ||
         in->type() == MIRType_Elements)
     {
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -189,16 +189,26 @@ class ToDoublePolicy : public BoxInputsP
 {
   public:
     static bool staticAdjustInputs(MInstruction *def);
     bool adjustInputs(MInstruction *def) {
         return staticAdjustInputs(def);
     }
 };
 
+// Box objects, strings and undefined as input to a ToInt32 instruction.
+class ToInt32Policy : public BoxInputsPolicy
+{
+  public:
+    static bool staticAdjustInputs(MInstruction *def);
+    bool adjustInputs(MInstruction *def) {
+        return staticAdjustInputs(def);
+    }
+};
+
 template <unsigned Op>
 class ObjectPolicy : public BoxInputsPolicy
 {
   public:
     static bool staticAdjustInputs(MInstruction *ins);
     bool adjustInputs(MInstruction *ins) {
         return staticAdjustInputs(ins);
     }
--- a/js/src/jsapi-tests/testArgumentsObject.cpp
+++ b/js/src/jsapi-tests/testArgumentsObject.cpp
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* 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 "jsapi-tests/tests.h"
 
+#include "jsobjinlines.h"
+
 #include "vm/ArgumentsObject-inl.h"
 
 using namespace js;
 
 static const char NORMAL_ZERO[] =
     "function f() { return arguments; }";
 static const char NORMAL_ONE[] =
     "function f(a) { return arguments; }";
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -68,17 +68,17 @@ BEGIN_TEST(testChromeBuffer)
                                                         "trusted", 1, &paramName, bytes, strlen(bytes),
                                                         "", 0));
             trusted_fun = JS_GetFunctionObject(fun);
             if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
                 return false;
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
-        CHECK(JS_WrapValue(cx, v.address()));
+        CHECK(JS_WrapValue(cx, &v));
 
         const char *paramName = "trusted";
         const char *bytes = "try {                                      "
                             "    return untrusted(trusted);             "
                             "} catch (e) {                              "
                             "    try {                                  "
                             "        return trusted(100);               "
                             "    } catch(e) {                           "
@@ -109,17 +109,17 @@ BEGIN_TEST(testChromeBuffer)
             JS::HandleObject global = JS::HandleObject::fromMarkedLocation(&trusted_glob);
             CHECK(fun = JS_CompileFunctionForPrincipals(cx, global, &system_principals,
                                                         "trusted", 1, &paramName, bytes, strlen(bytes),
                                                         "", 0));
             trusted_fun = JS_GetFunctionObject(fun);
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
-        CHECK(JS_WrapValue(cx, v.address()));
+        CHECK(JS_WrapValue(cx, &v));
 
         const char *paramName = "trusted";
         const char *bytes = "try {                                      "
                             "  return untrusted(trusted);               "
                             "} catch (e) {                              "
                             "  return trusted(untrusted);               "
                             "}                                          ";
         CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1150,26 +1150,22 @@ JS_WrapObject(JSContext *cx, MutableHand
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (objp)
         JS::ExposeGCThingToActiveJS(objp, JSTRACE_OBJECT);
     return cx->compartment()->wrap(cx, objp);
 }
 
 JS_PUBLIC_API(bool)
-JS_WrapValue(JSContext *cx, jsval *vp)
+JS_WrapValue(JSContext *cx, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    if (vp)
-        JS::ExposeValueToActiveJS(*vp);
-    RootedValue value(cx, *vp);
-    bool ok = cx->compartment()->wrap(cx, &value);
-    *vp = value.get();
-    return ok;
+    JS::ExposeValueToActiveJS(vp);
+    return cx->compartment()->wrap(cx, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_WrapId(JSContext *cx, jsid *idp)
 {
   AssertHeapIsIdle(cx);
   CHECK_REQUEST(cx);
   if (idp) {
@@ -4542,16 +4538,21 @@ JS::CompileOffThread(JSContext *cx, Hand
 #endif
 }
 
 JS_PUBLIC_API(JSScript *)
 JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token)
 {
 #ifdef JS_WORKER_THREADS
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
+
+    Maybe<AutoLastFrameCheck> lfc;
+    if (maybecx)
+        lfc.construct(maybecx);
+
     return rt->workerThreadState->finishParseTask(maybecx, rt, token);
 #else
     MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
 #endif
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *objArg, JSPrincipals *principals,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1756,17 +1756,17 @@ JS_SetCompartmentPrivate(JSCompartment *
 
 extern JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSCompartment *compartment);
 
 extern JS_PUBLIC_API(bool)
 JS_WrapObject(JSContext *cx, JS::MutableHandleObject objp);
 
 extern JS_PUBLIC_API(bool)
-JS_WrapValue(JSContext *cx, jsval *vp);
+JS_WrapValue(JSContext *cx, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_WrapId(JSContext *cx, jsid *idp);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_TransplantObject(JSContext *cx, JS::Handle<JSObject*> origobj, JS::Handle<JSObject*> target);
 
 extern JS_PUBLIC_API(bool)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1099,17 +1099,17 @@ Evaluate(JSContext *cx, unsigned argc, j
                     return false;
                 args.rval().setString(str);
                 return true;
             }
             return false;
         }
     }
 
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 static JSString *
 FileAsString(JSContext *cx, const char *pathname)
 {
     FILE *file;
     RootedString str(cx);
     size_t len, cc;
@@ -3772,17 +3772,17 @@ DecompileThisScript(JSContext *cx, unsig
     {
         JSAutoCompartment ac(cx, script);
         JSString *result = JS_DecompileScript(cx, script, "test", 0);
         if (!result)
             return false;
         args.rval().setString(result);
     }
 
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 static bool
 ThisFilename(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script (cx);
     if (!JS_DescribeScriptedCaller(cx, &script, nullptr) || !script->filename()) {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -391,17 +391,17 @@ js::RunScript(JSContext *cx, RunState &s
     SPSEntryMarker marker(cx->runtime());
 
 #ifdef JS_ION
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
             return false;
         if (status == jit::Method_Compiled) {
-            jit::IonExecStatus status = jit::Cannon(cx, state);
+            jit::IonExecStatus status = jit::IonCannon(cx, state);
             return !IsErrorStatus(status);
         }
     }
 
     if (jit::IsBaselineEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
         if (status == jit::Method_Error)
             return false;
@@ -2484,17 +2484,17 @@ BEGIN_CASE(JSOP_FUNCALL)
         if (newType)
             state.setUseNewType();
 
         if (!newType && jit::IsIonEnabled(cx)) {
             jit::MethodStatus status = jit::CanEnter(cx, state);
             if (status == jit::Method_Error)
                 goto error;
             if (status == jit::Method_Compiled) {
-                jit::IonExecStatus exec = jit::Cannon(cx, state);
+                jit::IonExecStatus exec = jit::IonCannon(cx, state);
                 CHECK_BRANCH();
                 regs.sp = args.spAfterCall();
                 interpReturnOK = !IsErrorStatus(exec);
                 goto jit_return;
             }
         }
 
         if (jit::IsBaselineEnabled(cx)) {
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -1024,16 +1024,21 @@ class AutoPropertyDescArray
 };
 
 } /* anonymous namespace */
 
 static const char *
 FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
 {
     RootedValue v(cx, vArg);
+
+    mozilla::Maybe<AutoCompartment> ac;
+    if (v.isObject())
+        ac.construct(cx, &v.toObject());
+
     JSString *str = ToString<CanGC>(cx, v);
     if (!str)
         return nullptr;
     const char *buf = bytes.encodeLatin1(cx, str);
     if (!buf)
         return nullptr;
     const char *found = strstr(buf, "function ");
     if (found && (found - buf <= 2))
@@ -1054,26 +1059,16 @@ FormatFrame(JSContext *cx, const NonBuil
 
     const char *filename = script->filename();
     unsigned lineno = PCToLineNumber(script, pc);
     RootedFunction fun(cx, iter.maybeCallee());
     RootedString funname(cx);
     if (fun)
         funname = fun->atom();
 
-    RootedObject callObj(cx);
-    AutoPropertyDescArray callProps(cx);
-
-    if (!iter.isJit() && (showArgs || showLocals)) {
-        JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
-        callObj = frame.callObject(cx);
-        if (callObj)
-            callProps.fetch(callObj);
-    }
-
     RootedValue thisVal(cx);
     AutoPropertyDescArray thisProps(cx);
     if (iter.computeThis(cx)) {
         thisVal = iter.thisv();
         if (showThisProps && !thisVal.isPrimitive())
             thisProps.fetch(&thisVal.toObject());
     }
 
@@ -1084,118 +1079,85 @@ FormatFrame(JSContext *cx, const NonBuil
     } else if (fun) {
         buf = JS_sprintf_append(buf, "%d anonymous(", num);
     } else {
         buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
     }
     if (!buf)
         return buf;
 
-    // print the function arguments
-    if (showArgs && callObj) {
-        uint32_t namedArgCount = 0;
-        for (uint32_t i = 0; i < callProps->length; i++) {
-            JSPropertyDesc* desc = &callProps->array[i];
+    if (showArgs && iter.hasArgs()) {
+        BindingVector bindings(cx);
+        if (fun && fun->isInterpreted()) {
+            if (!FillBindingVector(script, &bindings))
+                return buf;
+        }
+
+
+        bool first = true;
+        for (unsigned i = 0; i < iter.numActualArgs(); i++) {
+            RootedValue arg(cx);
+            if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
+                for (AliasedFormalIter fi(script); ; fi++) {
+                    if (fi.frameIndex() == i) {
+                        arg = iter.callObj().aliasedVar(fi);
+                        break;
+                    }
+                }
+            } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
+                arg = iter.argsObj().arg(i);
+            } else {
+                arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
+            }
+
+            JSAutoByteString valueBytes;
+            const char *value = FormatValue(cx, arg, valueBytes);
+
             JSAutoByteString nameBytes;
             const char *name = nullptr;
-            bool hasName = JSVAL_IS_STRING(desc->id);
-            if (hasName)
-                name = FormatValue(cx, desc->id, nameBytes);
-            JSAutoByteString valueBytes;
-            const char *value = FormatValue(cx, desc->value, valueBytes);
 
-            if (value && (name || !hasName)) {
+            if (i < bindings.length()) {
+                name = nameBytes.encodeLatin1(cx, bindings[i].name());
+                if (!buf)
+                    return NULL;
+            }
+
+            if (value) {
                 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
-                                        namedArgCount ? ", " : "",
+                                        !first ? ", " : "",
                                         name ? name :"",
                                         name ? " = " : "",
-                                        desc->value.isString() ? "\"" : "",
+                                        arg.isString() ? "\"" : "",
                                         value ? value : "?unknown?",
-                                        desc->value.isString() ? "\"" : "");
+                                        arg.isString() ? "\"" : "");
                 if (!buf)
                     return buf;
+
+                first = false;
             } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get named argument while inspecting stack frame>\n");
+                buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
+                if (!buf)
+                    return buf;
                 cx->clearPendingException();
 
             }
-            namedArgCount++;
-        }
-
-        // print any unnamed trailing args (found in 'arguments' object)
-        RootedValue val(cx);
-        if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) {
-            uint32_t argCount;
-            RootedObject argsObj(cx, &val.toObject());
-            if (JS_GetProperty(cx, argsObj, "length", &val) &&
-                ToUint32(cx, val, &argCount) &&
-                argCount > namedArgCount)
-            {
-                for (uint32_t k = namedArgCount; k < argCount; k++) {
-                    char number[8];
-                    JS_snprintf(number, 8, "%d", (int) k);
-
-                    JSAutoByteString valueBytes;
-                    const char *value = nullptr;
-                    if (JS_GetProperty(cx, argsObj, number, &val) &&
-                        (value = FormatValue(cx, val, valueBytes)))
-                    {
-                        buf = JS_sprintf_append(buf, "%s%s%s%s",
-                                                k ? ", " : "",
-                                                val.isString() ? "\"" : "",
-                                                value ? value : "?unknown?",
-                                                val.isString() ? "\"" : "");
-                        if (!buf)
-                            return buf;
-                    } else {
-                        buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
-                        cx->clearPendingException();
-                    }
-                }
-            } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get 'length' while inspecting stack frame>\n");
-                cx->clearPendingException();
-            }
-        } else {
-            buf = JS_sprintf_append(buf, "    <Failed to get 'arguments' while inspecting stack frame>\n");
-            cx->clearPendingException();
         }
     }
 
     // print filename and line number
     buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
                             fun ? ")" : "",
                             filename ? filename : "<unknown>",
                             lineno);
     if (!buf)
         return buf;
 
-    // print local variables
-    if (showLocals && callProps->array) {
-        for (uint32_t i = 0; i < callProps->length; i++) {
-            JSPropertyDesc* desc = &callProps->array[i];
-            JSAutoByteString nameBytes;
-            JSAutoByteString valueBytes;
-            const char *name = FormatValue(cx, desc->id, nameBytes);
-            const char *value = FormatValue(cx, desc->value, valueBytes);
 
-            if (name && value) {
-                buf = JS_sprintf_append(buf, "    %s = %s%s%s\n",
-                                        name,
-                                        desc->value.isString() ? "\"" : "",
-                                        value,
-                                        desc->value.isString() ? "\"" : "");
-                if (!buf)
-                    return buf;
-            } else {
-                buf = JS_sprintf_append(buf, "    <Failed to get local while inspecting stack frame>\n");
-                cx->clearPendingException();
-            }
-        }
-    }
+    // Note: Right now we don't dump the local variables anymore, because
+    // that is hard to support across all the JITs etc.
 
     // print the value of 'this'
     if (showLocals) {
         if (!thisVal.isUndefined()) {
             JSAutoByteString thisValBytes;
             RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
             const char *str = nullptr;
             if (thisValStr &&
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1614,17 +1614,17 @@ NewObjectCache::invalidateEntriesForShap
 /* static */ void
 EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto)
 {
     InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
                                      shape->getObjectParent(), shape->getObjectMetadata(),
                                      shape->numFixedSlots(), shape->getObjectFlags());
 
     /* Bug 929547 - we do not rekey based on metadata object moves */
-    JSObject *metadata = shape->getObjectMetadata();
+    DebugOnly<JSObject *> metadata = shape->getObjectMetadata();
     JS_ASSERT_IF(metadata, !gc::IsInsideNursery(cx->compartment()->runtimeFromAnyThread(), metadata));
 
     InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup);
     JS_ASSERT(p);
 
     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
 
     /* The new shape had better be rooted at the old one. */
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -1069,17 +1069,17 @@ mozJSComponentLoader::Import(const nsACS
         if (targetVal.isObject()) {
             // If we're passing in something like a content DOM window, chances
             // are the caller expects the properties to end up on the object
             // proper and not on the Xray holder. This is dubious, but can be used
             // during testing. Given that dumb callers can already leak JSMs into
             // content by passing a raw content JS object (where Xrays aren't
             // possible), we aim for consistency here. Waive xray.
             if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
-                !WrapperFactory::WaiveXrayAndWrap(cx, targetVal.address()))
+                !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
             {
                 return NS_ERROR_FAILURE;
             }
             targetObject = &targetVal.toObject();
         } else if (!targetVal.isNull()) {
             // If targetVal isNull(), we actually want to leave targetObject null.
             // Not doing so breaks |make package|.
             return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
@@ -1202,17 +1202,17 @@ mozJSComponentLoader::ImportInto(const n
                                &newEntry->location, true, &exception);
 
         mInProgressImports.Remove(key);
 
         if (NS_FAILED(rv)) {
             if (!exception.isUndefined()) {
                 // An exception was thrown during compilation. Propagate it
                 // out to our caller so they can report it.
-                if (!JS_WrapValue(callercx, exception.address()))
+                if (!JS_WrapValue(callercx, &exception))
                     return NS_ERROR_OUT_OF_MEMORY;
                 JS_SetPendingException(callercx, exception);
                 return NS_OK;
             }
 
             // Something failed, but we don't know what it is, guess.
             return NS_ERROR_FILE_NOT_FOUND;
         }
@@ -1276,17 +1276,17 @@ mozJSComponentLoader::ImportInto(const n
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
 
             JSAutoCompartment target_ac(mContext, targetObj);
 
-            if (!JS_WrapValue(mContext, value.address()) ||
+            if (!JS_WrapValue(mContext, &value) ||
                 !JS_SetPropertyById(mContext, targetObj, symbolId, value)) {
                 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
                 if (!bytes)
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -305,27 +305,29 @@ mozJSSubScriptLoader::LoadSubScript(cons
         return rv;
 
     if (function) {
         script = JS_GetFunctionScript(cx, function);
     }
 
     loader->NoteSubScript(script, targetObj);
 
+    RootedValue rval(cx);
     bool ok = false;
     if (function) {
-        ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, retval);
+        ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, rval.address());
     } else {
-        ok = JS_ExecuteScriptVersion(cx, targetObj, script, retval, version);
+        ok = JS_ExecuteScriptVersion(cx, targetObj, script, rval.address(), version);
     }
 
     if (ok) {
         JSAutoCompartment rac(cx, result_obj);
-        if (!JS_WrapValue(cx, retval))
+        if (!JS_WrapValue(cx, &rval))
             return NS_ERROR_UNEXPECTED;
+        *retval = rval;
     }
 
     if (cache && ok && writeScript) {
         WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
     }
 
     return NS_OK;
 }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -300,17 +300,17 @@ ExportFunction(JSContext *cx, unsigned a
         // we have to add it to the target scope as a property.
         if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(),
                                    JS_PropertyStub, JS_StrictPropertyStub,
                                    JSPROP_ENUMERATE))
             return false;
     }
 
     // Finally we have to re-wrap the exported function back to the caller compartment.
-    if (!JS_WrapValue(cx, args.rval().address()))
+    if (!JS_WrapValue(cx, args.rval()))
         return false;
 
     return true;
 }
 
 static bool
 GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
 {
@@ -977,17 +977,18 @@ xpc::GlobalProperties::Define(JSContext 
     if (btoa &&
         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
         return false;
 
     return true;
 }
 
 nsresult
-xpc::CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options)
+xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop,
+                         SandboxOptions& options)
 {
     // Create the sandbox global object
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if (NS_FAILED(rv))
         return NS_ERROR_XPC_UNEXPECTED;
 
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
@@ -1037,17 +1038,17 @@ xpc::CreateSandboxObject(JSContext *cx, 
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
             if (!ok)
                 return NS_ERROR_XPC_UNEXPECTED;
 
             if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) {
                 RootedValue v(cx, ObjectValue(*options.proto));
-                if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+                if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
                     return NS_ERROR_FAILURE;
                 options.proto = &v.toObject();
             }
 
             // Now check what sort of thing we've got in |proto|
             JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false);
             const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
             if (IS_WN_CLASS(unwrappedClass) ||
@@ -1089,26 +1090,25 @@ xpc::CreateSandboxObject(JSContext *cx, 
              !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) ||
              !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0)))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (!options.globalProperties.Define(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
     }
 
-    if (vp) {
-        // We have this crazy behavior where wantXrays=false also implies that the
-        // returned sandbox is implicitly waived. We've stopped advertising it, but
-        // keep supporting it for now.
-        *vp = OBJECT_TO_JSVAL(sandbox);
-        if (options.wantXrays && !JS_WrapValue(cx, vp))
-            return NS_ERROR_UNEXPECTED;
-        if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
-            return NS_ERROR_UNEXPECTED;
-    }
+
+    // We have this crazy behavior where wantXrays=false also implies that the
+    // returned sandbox is implicitly waived. We've stopped advertising it, but
+    // keep supporting it for now.
+    vp.setObject(*sandbox);
+    if (options.wantXrays && !JS_WrapValue(cx, vp))
+        return NS_ERROR_UNEXPECTED;
+    if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
+        return NS_ERROR_UNEXPECTED;
 
     // Set the location information for the new global, so that tools like
     // about:memory may use that information
     xpc::SetLocationForGlobal(sandbox, options.sandboxName);
 
     xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
 
     JS_FireOnNewGlobalObject(cx, sandbox);
@@ -1504,17 +1504,17 @@ nsXPCComponents_utils_Sandbox::CallOrCon
 
     SandboxOptions options(cx, optionsObject);
     if (calledWithOptions && !options.Parse())
         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
     if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
-    rv = CreateSandboxObject(cx, args.rval().address(), prinOrSop, options);
+    rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
 
     if (NS_FAILED(rv))
         return ThrowAndFail(rv, cx, _retval);
 
     *_retval = true;
     return NS_OK;
 }
 
@@ -1642,29 +1642,29 @@ xpc::EvalInSandbox(JSContext *cx, Handle
     //
     // Alright, we're back on the caller's cx. If an error occured, try to
     // wrap and set the exception. Otherwise, wrap the return value.
     //
 
     if (!ok) {
         // If we end up without an exception, it was probably due to OOM along
         // the way, in which case we thow. Otherwise, wrap it.
-        if (exn.isUndefined() || !JS_WrapValue(cx, exn.address()))
+        if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
             return NS_ERROR_OUT_OF_MEMORY;
 
         // Set the exception on our caller's cx.
         JS_SetPendingException(cx, exn);
         return NS_ERROR_FAILURE;
     }
 
     // Transitively apply Xray waivers if |sb| was waived.
     if (waiveXray) {
-        ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address());
+        ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
     } else {
-        ok = JS_WrapValue(cx, v.address());
+        ok = JS_WrapValue(cx, &v);
     }
     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
     // Whew!
     rval.set(v);
     return NS_OK;
 }
 
@@ -1705,22 +1705,23 @@ CloningFunctionForwarder(JSContext *cx, 
             }
         }
 
         // JS API does not support any JSObject to JSFunction conversion,
         // so let's use JS_CallFunctionValue instead.
         RootedValue functionVal(cx);
         functionVal.setObject(*origFunObj);
 
-        if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp))
+        if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(),
+                                  args.rval().address()))
             return false;
     }
 
     // Return value must be wrapped.
-    return JS_WrapValue(cx, vp);
+    return JS_WrapValue(cx, args.rval());
 }
 
 bool
 xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
                           MutableHandleValue vp)
 {
     JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
                                                                     NonCloningFunctionForwarder,
@@ -1743,17 +1744,17 @@ xpc::GetSandboxMetadata(JSContext *cx, H
     MOZ_ASSERT(IsSandbox(sandbox));
 
     RootedValue metadata(cx);
     {
       JSAutoCompartment ac(cx, sandbox);
       metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
     }
 
-    if (!JS_WrapValue(cx, metadata.address()))
+    if (!JS_WrapValue(cx, &metadata))
         return NS_ERROR_UNEXPECTED;
 
     rval.set(metadata);
     return NS_OK;
 }
 
 nsresult
 xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3208,38 +3208,38 @@ nsXPCComponents_Utils::SetWantXrays(cons
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetComponentsForScope(const jsval &vscope, JSContext *cx,
                                              jsval *rval)
 {
     if (!vscope.isObject())
         return NS_ERROR_INVALID_ARG;
     JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject());
     XPCWrappedNativeScope *scope = GetObjectScope(scopeObj);
-    JSObject *components = scope->GetComponentsJSObject();
+    RootedObject components(cx, scope->GetComponentsJSObject());
     if (!components)
         return NS_ERROR_FAILURE;
+    if (!JS_WrapObject(cx, &components))
+        return NS_ERROR_FAILURE;
     *rval = ObjectValue(*components);
-    if (!JS_WrapValue(cx, rval))
-        return NS_ERROR_FAILURE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::Dispatch(const jsval &runnableArg, const jsval &scope,
                                 JSContext *cx)
 {
     // Enter the given compartment, if any, and rewrap runnable.
     Maybe<JSAutoCompartment> ac;
     RootedValue runnable(cx, runnableArg);
     if (scope.isObject()) {
         JSObject *scopeObj = js::UncheckedUnwrap(&scope.toObject());
         if (!scopeObj)
             return NS_ERROR_FAILURE;
         ac.construct(cx, scopeObj);
-        if (!JS_WrapValue(cx, runnable.address()))
+        if (!JS_WrapValue(cx, &runnable))
             return NS_ERROR_FAILURE;
     }
 
     // Get an XPCWrappedJS for |runnable|.
     if (!runnable.isObject())
         return NS_ERROR_INVALID_ARG;
 
     nsCOMPtr<nsIRunnable> run;
@@ -3338,33 +3338,35 @@ nsXPCComponents_Utils::IsXrayWrapper(con
     *aRetval =
         obj.isObject() && xpc::WrapperFactory::IsXrayWrapper(&obj.toObject());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::WaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval)
 {
-    *aRetval = aVal;
-    if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, aRetval))
+    RootedValue value(aCx, aVal);
+    if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, &value))
         return NS_ERROR_FAILURE;
+    *aRetval = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::UnwaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval)
 {
     if (!aVal.isObject()) {
         *aRetval = aVal;
         return NS_OK;
     }
 
-    *aRetval = ObjectValue(*js::UncheckedUnwrap(&aVal.toObject()));
-    if (!JS_WrapValue(aCx, aRetval))
+    RootedObject obj(aCx, js::UncheckedUnwrap(&aVal.toObject()));
+    if (!JS_WrapObject(aCx, &obj))
         return NS_ERROR_FAILURE;
+    *aRetval = ObjectValue(*obj);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetClassName(const Value &aObj, bool aUnwrap, JSContext *aCx, char **aRv)
 {
     if (!aObj.isObject())
         return NS_ERROR_INVALID_ARG;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -150,17 +150,17 @@ XPCConvert::NativeData2JS(MutableHandleV
                 return false;
             d.setString(str);
             break;
         }
 
     case nsXPTType::T_JSVAL :
         {
             d.set(*((Value*)s));
-            if (!JS_WrapValue(cx, d.address()))
+            if (!JS_WrapValue(cx, d))
                 return false;
             break;
         }
 
     default:
 
         // set the default result
         d.setNull();
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3373,20 +3373,17 @@ XPCJSRuntime::RemoveContextCallback(xpcC
 JSObject *
 XPCJSRuntime::GetJunkScope()
 {
     if (!mJunkScope) {
         AutoSafeJSContext cx;
         SandboxOptions options;
         options.sandboxName.AssignASCII("XPConnect Junk Compartment");
         RootedValue v(cx);
-        nsresult rv = CreateSandboxObject(cx, v.address(),
-                                          nsContentUtils::GetSystemPrincipal(),
-                                          options);
-
+        nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options);
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         mJunkScope = js::UncheckedUnwrap(&v.toObject());
         JS_AddNamedObjectRoot(cx, &mJunkScope, "XPConnect Junk Compartment");
     }
     return mJunkScope;
 }
 
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -386,29 +386,29 @@ XPCVariant::VariantDataToJS(nsIVariant* 
     RootedValue realVal(cx);
     nsresult rv = variant->GetAsJSVal(realVal.address());
 
     if (NS_SUCCEEDED(rv) &&
         (JSVAL_IS_PRIMITIVE(realVal) ||
          type == nsIDataType::VTYPE_ARRAY ||
          type == nsIDataType::VTYPE_EMPTY_ARRAY ||
          type == nsIDataType::VTYPE_ID)) {
-        if (!JS_WrapValue(cx, realVal.address()))
+        if (!JS_WrapValue(cx, &realVal))
             return false;
         pJSVal.set(realVal);
         return true;
     }
 
     nsCOMPtr<XPCVariant> xpcvariant = do_QueryInterface(variant);
     if (xpcvariant && xpcvariant->mReturnRawObject) {
         MOZ_ASSERT(type == nsIDataType::VTYPE_INTERFACE ||
                    type == nsIDataType::VTYPE_INTERFACE_IS,
                    "Weird variant");
 
-        if (!JS_WrapValue(cx, realVal.address()))
+        if (!JS_WrapValue(cx, &realVal))
             return false;
         pJSVal.set(realVal);
         return true;
     }
 
     // else, it's an object and we really need to double wrap it if we've
     // already decided that its 'natural' type is as some sort of interface.
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -129,32 +129,34 @@ GetDoubleWrappedJSObject(XPCCallContext&
 }
 
 // This is the getter native function we use to handle 'wrappedJSObject' for
 // double wrapped JSObjects.
 
 static bool
 XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp)
 {
+    CallArgs args = CallArgsFromVp(argc, vp);
+
     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
     if (!obj)
         return false;
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     MOZ_ASSERT(JS_TypeOfValue(cx, JS_CALLEE(cx, vp)) == JSTYPE_FUNCTION, "bad function");
 
     RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
     if (!realObject) {
         // This is pretty unexpected at this point. The object originally
         // responded to this get property call and now gives no object.
         // XXX Should this throw something at the caller?
-        *vp = JSVAL_NULL;
+        args.rval().setNull();
         return true;
     }
 
     // It is a double wrapped object. Figure out if the caller
     // is allowed to see it.
 
     nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
     if (sm) {
@@ -173,18 +175,19 @@ XPC_WN_DoubleWrappedGetter(JSContext *cx
                                     wrapper->GetIdentityObject(),
                                     wrapper->GetClassInfo(), id,
                                     wrapper->GetSecurityInfoAddr()))) {
                 // The SecurityManager should have set an exception.
                 return false;
             }
         }
     }
-    *vp = OBJECT_TO_JSVAL(realObject);
-    return JS_WrapValue(cx, vp);
+
+    args.rval().setObject(*realObject);
+    return JS_WrapValue(cx, args.rval());
 }
 
 /***************************************************************************/
 
 // This is our shared function to define properties on our JSObjects.
 
 /*
  * NOTE:
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -255,18 +255,18 @@ XPCWrappedNativeScope::EnsureXBLScope(JS
     nsIPrincipal *principal = GetPrincipal();
     nsCOMPtr<nsIExpandedPrincipal> ep;
     MOZ_ASSERT(!(ep = do_QueryInterface(principal)));
     nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
     principalAsArray.AppendElement(principal);
     ep = new nsExpandedPrincipal(principalAsArray);
 
     // Create the sandbox.
-    JS::RootedValue v(cx, JS::UndefinedValue());
-    nsresult rv = CreateSandboxObject(cx, v.address(), ep, options);
+    RootedValue v(cx);
+    nsresult rv = CreateSandboxObject(cx, &v, ep, options);
     NS_ENSURE_SUCCESS(rv, nullptr);
     mXBLScope = &v.toObject();
 
     // Tag it.
     EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
 
     // Good to go!
     return mXBLScope;
--- a/js/xpconnect/src/XPCWrapper.cpp
+++ b/js/xpconnect/src/XPCWrapper.cpp
@@ -32,39 +32,39 @@ UnwrapNW(JSContext *cx, unsigned argc, j
 
   JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]);
   if (!v.isObject() || !js::IsWrapper(&v.toObject())) {
     JS_SET_RVAL(cx, vp, v);
     return true;
   }
 
   if (AccessCheck::wrapperSubsumes(&v.toObject())) {
-    bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address());
+    bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
     NS_ENSURE_TRUE(ok, false);
   }
 
   JS_SET_RVAL(cx, vp, v);
   return true;
 }
 
 static bool
 XrayWrapperConstructor(JSContext *cx, unsigned argc, jsval *vp)
 {
-  if (argc == 0) {
+  JS::CallArgs args = CallArgsFromVp(argc, vp);
+  if (args.length() == 0) {
     return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
   }
 
-  JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]);
-  if (!v.isObject()) {
-    JS_SET_RVAL(cx, vp, v);
+  if (!args[0].isObject()) {
+    args.rval().set(args[0]);
     return true;
   }
 
-  *vp = JS::ObjectValue(*js::UncheckedUnwrap(&v.toObject()));
-  return JS_WrapValue(cx, vp);
+  args.rval().setObject(*js::UncheckedUnwrap(&args[0].toObject()));
+  return JS_WrapValue(cx, args.rval());
 }
 // static
 bool
 AttachNewConstructorObject(JSContext *aCx, JS::HandleObject aGlobalObject)
 {
   // Pushing a JSContext calls ActivateDebugger which calls this function, so
   // we can't use an AutoJSContext here until JSD is gone.
   JSAutoCompartment ac(aCx, aGlobalObject);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -908,20 +908,19 @@ nsXPConnect::ClearAllWrappedNativeSecuri
 }
 
 NS_IMETHODIMP
 nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal,
                            nsIXPConnectJSObjectHolder **_retval)
 {
     *_retval = nullptr;
 
-    RootedValue rval(cx, JSVAL_VOID);
-
+    RootedValue rval(cx);
     SandboxOptions options;
-    nsresult rv = CreateSandboxObject(cx, rval.address(), principal, options);
+    nsresult rv = CreateSandboxObject(cx, &rval, principal, options);
     MOZ_ASSERT(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval),
                "Bad return value from xpc_CreateSandboxObject()!");
 
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) {
         *_retval = XPCJSObjectHolder::newHolder(JSVAL_TO_OBJECT(rval));
         NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY);
 
         NS_ADDREF(*_retval);
--- a/js/xpconnect/src/qsgen.py
+++ b/js/xpconnect/src/qsgen.py
@@ -674,17 +674,17 @@ resultConvTemplates = {
     '[astring]':
         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
 
     '[domstring]':
         "    return xpc::StringToJsval(cx, result, ${jsvalPtr});\n",
 
     '[jsval]':
         "    ${jsvalPtr}.set(result);\n"
-        "    return JS_WrapValue(cx, ${jsvalPtr}.address());\n"
+        "    return JS_WrapValue(cx, ${jsvalPtr});\n"
     }
 
 def isVariantType(t):
     return isSpecificInterfaceType(t, 'nsIVariant')
 
 def writeResultConv(f, type, jsvalPtr, jsvalRef):
     """ Emit code to convert the C++ variable `result` to a jsval.
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3687,17 +3687,17 @@ CreateGlobalObject(JSContext *cx, const 
 // system using EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be
 // rooted by the caller), and uses the principal that's either
 // directly passed in prinOrSop or indirectly as an
 // nsIScriptObjectPrincipal holding the principal. If no principal is
 // reachable through prinOrSop, a new null principal will be created
 // and used.
 nsresult
-CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop,
+CreateSandboxObject(JSContext *cx, JS::MutableHandleValue vp, nsISupports *prinOrSop,
                     xpc::SandboxOptions& options);
 // Helper for evaluating scripts in a sandbox object created with
 // CreateSandboxObject(). The caller is responsible of ensuring
 // that *rval doesn't get collected during the call or usage after the
 // call. This helper will use filename and lineNo for error reporting,
 // and if no filename is provided it will use the codebase from the
 // principal and line number 1 as a fallback. if returnStringOnly is
 // true, then the result in *rval, or the exception in cx->exception
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -32,16 +32,18 @@ support-files =
   file_mozMatchesSelector.html
   file_nodelists.html
   file_wrappers-2.html
   inner.html
   test1_bug629331.html
   test2_bug629331.html
 
 [test_asmjs.html]
+# bug 929498
+skip-if = os == 'android'
 [test_bug384632.html]
 [test_bug390488.html]
 [test_bug393269.html]
 [test_bug396851.html]
 [test_bug428021.html]
 [test_bug446584.html]
 [test_bug462428.html]
 [test_bug478438.html]
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
@@ -13,24 +13,24 @@ using namespace JS;
 
 namespace xpc {
 
 static bool
 WaiveAccessors(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
 {
     if (desc.hasGetterObject() && desc.getterObject()) {
         RootedValue v(cx, JS::ObjectValue(*desc.getterObject()));
-        if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+        if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
             return false;
         desc.setGetterObject(&v.toObject());
     }
 
     if (desc.hasSetterObject() && desc.setterObject()) {
         RootedValue v(cx, JS::ObjectValue(*desc.setterObject()));
-        if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address()))
+        if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
             return false;
         desc.setSetterObject(&v.toObject());
     }
     return true;
 }
 
 WaiveXrayWrapper::WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags)
 {
@@ -41,54 +41,54 @@ WaiveXrayWrapper::~WaiveXrayWrapper()
 }
 
 bool
 WaiveXrayWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper,
                                         HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc,
                                         unsigned flags)
 {
     return CrossCompartmentWrapper::getPropertyDescriptor(cx, wrapper, id, desc, flags) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc);
+           WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
 }
 
 bool
 WaiveXrayWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper,
                                            HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc,
                                            unsigned flags)
 {
     return CrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc);
+           WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
 }
 
 bool
 WaiveXrayWrapper::get(JSContext *cx, HandleObject wrapper,
                       HandleObject receiver, HandleId id,
                       MutableHandleValue vp)
 {
     return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, vp.address());
+           WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
 WaiveXrayWrapper::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args)
 {
     return CrossCompartmentWrapper::call(cx, wrapper, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 bool
 WaiveXrayWrapper::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args)
 {
     return CrossCompartmentWrapper::construct(cx, wrapper, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 // NB: This is important as the other side of a handshake with FieldGetter. See
 // nsXBLProtoImplField.cpp.
 bool
 WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test,
                              JS::NativeImpl impl, JS::CallArgs args)
 {
     return CrossCompartmentWrapper::nativeCall(cx, test, impl, args) &&
-           WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address());
+           WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
 
 }
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -539,25 +539,25 @@ WrapperFactory::WrapForSameCompartment(J
     return wrapper;
 }
 
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
-WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp)
+WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
 {
-    if (JSVAL_IS_PRIMITIVE(*vp))
+    if (vp.isPrimitive())
         return JS_WrapValue(cx, vp);
 
-    JSObject *obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(*vp));
+    JSObject *obj = js::UncheckedUnwrap(&vp.toObject());
     MOZ_ASSERT(!js::IsInnerObject(obj));
     if (js::IsObjectInContextCompartment(obj, cx)) {
-        *vp = OBJECT_TO_JSVAL(obj);
+        vp.setObject(*obj);
         return true;
     }
 
     // Even though waivers have no effect on access by scopes that don't subsume
     // the underlying object, good defense-in-depth dictates that we should avoid
     // handing out waivers to callers that can't use them. The transitive waiving
     // machinery unconditionally calls WaiveXrayAndWrap on return values from
     // waived functions, even though the return value might be not be same-origin
@@ -565,17 +565,17 @@ WrapperFactory::WaiveXrayAndWrap(JSConte
     // |cx|, we should check whether the caller has any business with waivers
     // to things in |obj|'s compartment.
     JSCompartment *target = js::GetContextCompartment(cx);
     JSCompartment *origin = js::GetObjectCompartment(obj);
     obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj;
     if (!obj)
         return false;
 
-    *vp = OBJECT_TO_JSVAL(obj);
+    vp.setObject(*obj);
     return JS_WrapValue(cx, vp);
 }
 
 JSObject *
 WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
     RootedObject proto(cx);
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -58,17 +58,17 @@ class WrapperFactory {
                             JS::HandleObject parent,
                             unsigned flags);
 
     // Wrap an object for same-compartment access.
     static JSObject *WrapForSameCompartment(JSContext *cx,
                                             JS::HandleObject obj);
 
     // Wrap wrapped object into a waiver wrapper and then re-wrap it.
-    static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp);
+    static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp);
 
     // Wrap a (same compartment) object in a SOW.
     static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);
 
     // Return true if this is a Components object.
     static bool IsComponentsObject(JSObject *obj);
 
     // Wrap a (same compartment) Components object.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -765,17 +765,17 @@ XPCWrappedNativeXrayTraits::resolveNativ
         // Without a wrapper the function would live on the prototype. Since we
         // don't have one, we have to avoid calling the scriptable helper's
         // GetProperty method for this property, so stub out the getter and
         // setter here explicitly.
         desc.setGetter(JS_PropertyStub);
         desc.setSetter(JS_StrictPropertyStub);
     }
 
-    if (!JS_WrapValue(cx, desc.value().address()) || !JS_WrapValue(cx, fval.address()))
+    if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval))
         return false;
 
     if (desc.hasGetterObject())
         desc.setGetterObject(&fval.toObject());
     if (desc.hasSetterObject())
         desc.setSetterObject(&fval.toObject());
 
     // Define the property.
@@ -788,17 +788,17 @@ wrappedJSObject_getter(JSContext *cx, Ha
 {
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "Unexpected object");
         return false;
     }
 
     vp.set(OBJECT_TO_JSVAL(wrapper));
 
-    return WrapperFactory::WaiveXrayAndWrap(cx, vp.address());
+    return WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
 XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
                                HandleObject wrapper, HandleObject holder, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc, unsigned flags)
 {
     desc.object().set(nullptr);
@@ -1100,17 +1100,17 @@ DOMXrayTraits::call(JSContext *cx, Handl
         if (!clasp->call(cx, args.length(), args.base()))
             return false;
     } else {
         // This is only reached for WebIDL instance objects, and in practice
         // only for plugins.  Just call them on the content compartment.
         if (!baseInstance.call(cx, wrapper, args))
             return false;
     }
-    return JS_WrapValue(cx, args.rval().address());
+    return JS_WrapValue(cx, args.rval());
 }
 
 bool
 DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper,
                          const JS::CallArgs &args, js::Wrapper& baseInstance)
 {
     RootedObject obj(cx, getTargetObject(wrapper));
     MOZ_ASSERT(mozilla::dom::HasConstructor(obj));
@@ -1122,17 +1122,17 @@ DOMXrayTraits::construct(JSContext *cx, 
             return false;
         }
         if (!clasp->construct(cx, args.length(), args.base()))
             return false;
     } else {
         if (!baseInstance.construct(cx, wrapper, args))
             return false;
     }
-    if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval().address()))
+    if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval()))
         return false;
     return true;
 }
 
 void
 DOMXrayTraits::preserveWrapper(JSObject *target)
 {
     nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target);
@@ -1225,36 +1225,43 @@ HasNativeProperty(JSContext *cx, HandleO
         return false;
     *hasProp = !!desc.object();
     return true;
 }
 
 } // namespace XrayUtils
 
 static bool
-XrayToString(JSContext *cx, unsigned argc, jsval *vp)
+XrayToString(JSContext *cx, unsigned argc, Value *vp)
 {
-    RootedObject  wrapper(cx, JS_THIS_OBJECT(cx, vp));
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args.thisv().isObject()) {
+        JS_ReportError(cx, "XrayToString called on an incompatible object");
+        return false;
+    }
+
+    RootedObject wrapper(cx, &args.thisv().toObject());
     if (!wrapper)
         return false;
     if (IsWrapper(wrapper) &&
         GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) {
         wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
     }
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
 
     static const char start[] = "[object XrayWrapper ";
     static const char end[] = "]";
     if (UseDOMXray(obj))
-        return NativeToString(cx, wrapper, obj, start, end, vp);
+        return NativeToString(cx, wrapper, obj, start, end, args.rval());
 
     nsAutoString result;
     result.AppendASCII(start);
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
     char *wrapperStr = wn->ToString();
     if (!wrapperStr) {
@@ -1266,17 +1273,17 @@ XrayToString(JSContext *cx, unsigned arg
 
     result.AppendASCII(end);
 
     JSString *str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>(result.get()),
                                         result.Length());
     if (!str)
         return false;
 
-    *vp = STRING_TO_JSVAL(str);
+    args.rval().setString(str);
     return true;
 }
 
 #ifdef DEBUG
 
 static void
 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
 {
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1236,22 +1236,16 @@ nsTextControlFrame::SetInitialChildList(
       statefulFrame->RestoreState(&fakePresState);
       Properties().Remove(ContentScrollPos());
       delete contentScrollPos;
     }
   }
   return rv;
 }
 
-bool
-nsTextControlFrame::IsScrollable() const
-{
-  return !IsSingleLineTextControl();
-}
-
 void
 nsTextControlFrame::SetValueChanged(bool aValueChanged)
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   if (mUsePlaceholder) {
     nsWeakFrame weakFrame(this);
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -34,18 +34,16 @@ public:
   NS_DECLARE_FRAME_PROPERTY(ContentScrollPos, DestroyPoint)
 
   nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext);
   virtual ~nsTextControlFrame();
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
 
   virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE {
-    if (!IsScrollable())
-      return nullptr;
     return do_QueryFrame(GetFirstPrincipalChild());
   }
 
   virtual nscoord GetMinWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
 
   virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext,
                                  nsSize aCBSize, nscoord aAvailableWidth,
@@ -230,23 +228,16 @@ protected:
 
   private:
     nsTextControlFrame* mFrame;
   };
 
   nsresult OffsetToDOMPoint(int32_t aOffset, nsIDOMNode** aResult, int32_t* aPosition);
 
   /**
-   * Find out whether this control is scrollable (i.e. if it is not a single
-   * line text control)
-   * @return whether this control is scrollable
-   */
-  bool IsScrollable() const;
-
-  /**
    * Update the textnode under our anonymous div to show the new
    * value. This should only be called when we have no editor yet.
    * @throws NS_ERROR_UNEXPECTED if the div has no text content
    */
   nsresult UpdateValueDisplay(bool aNotify,
                               bool aBeforeEditorInit = false,
                               const nsAString *aValue = nullptr);
 
--- a/layout/forms/test/mochitest.ini
+++ b/layout/forms/test/mochitest.ini
@@ -27,13 +27,14 @@ support-files =
 [test_bug571352.html]
 [test_bug572406.html]
 [test_bug572649.html]
 [test_bug595310.html]
 [test_bug620936.html]
 [test_bug644542.html]
 [test_bug672810.html]
 [test_bug704049.html]
+[test_bug717878_input_scroll.html]
 [test_bug869314.html]
 [test_bug903715.html]
 [test_listcontrol_search.html]
 [test_select_prevent_default.html]
 [test_textarea_resize.html]
new file mode 100644
--- /dev/null
+++ b/layout/forms/test/test_bug717878_input_scroll.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717878
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 717878</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717878">Mozilla Bug 717878</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<!-- size=10 and monospace font ensure there's no overflow in either direction -->
+<input id="no-overflow" type="text"
+  size="10"
+  style="
+    font-family: monospace;
+    font-size: 1em;"
+  value="Short">
+<!-- size=10, monospace font, and height=0.5em ensure overflow in both directions -->
+<input id="overflow" type="text"
+  size="10"
+  style="
+    font-family: monospace;
+    font-size: 1em;
+    height: 0.5em;"
+  value="This is a long string">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 717878 **/
+
+/**
+ * Test an element's scroll properties for correctness
+ *
+ * @param element Element to test
+ * @param prop Specify the property to test,
+ *             i.e. "scrollLeft" or "scrollTop"
+ * @param propMax Specify the scrollMax property to test,
+ *                i.e. "scrollLeftMax" or "scrollTopMax"
+ * @param is_overflow Specify whether the element is
+ *                    scrollable in the above direction
+ */
+function test_scroll(element, scroll, scrollMax, is_overflow) {
+
+  is(element[scroll], 0, element.id + " initial " + scroll + " != 0");
+  if (is_overflow) {
+    isnot(element[scrollMax], 0, element.id + " " + scrollMax + " == 0");
+  } else {
+    is(element[scrollMax], 0, element.id + " " + scrollMax + " != 0");
+  }
+
+  element[scroll] = 10;
+  if (is_overflow) {
+    isnot(element[scroll], 0, element.id + " unable to scroll " + scroll);
+  } else {
+    is(element[scroll], 0, element.id + " able to scroll " + scroll);
+  }
+
+  element[scroll] = element[scrollMax];
+  is(element[scroll], element[scrollMax], element.id + " did not scroll to " + scrollMax);
+
+  element[scroll] = element[scrollMax] + 10;
+  is(element[scroll], element[scrollMax], element.id + " scrolled past " + scrollMax);
+}
+
+var no_overflow = document.getElementById("no-overflow");
+test_scroll(no_overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ false);
+test_scroll(no_overflow, "scrollTop", "scrollTopMax", /* is_overflow */ false);
+
+var overflow = document.getElementById("overflow");
+test_scroll(overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ true);
+test_scroll(overflow, "scrollTop", "scrollTopMax", /* is_overflow */ true);
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5237,18 +5237,18 @@ nsFrame::UpdateOverflow()
 {
   MOZ_ASSERT(!(mState & NS_FRAME_SVG_LAYOUT) ||
              !(mState & NS_FRAME_IS_NONDISPLAY),
              "Non-display SVG do not maintain visual overflow rects");
 
   nsRect rect(nsPoint(0, 0), GetSize());
   nsOverflowAreas overflowAreas(rect, rect);
 
-  bool isBox = IsBoxFrame() || IsBoxWrapped();
-  if (!isBox || (!IsCollapsed() && !DoesClipChildren())) {
+  if (!DoesClipChildren() &&
+      !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) {
     nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
   }
 
   if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
     nsView* view = GetView();
     if (view) {
       uint32_t flags = 0;
       GetLayoutFlags(flags);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/928607-1-ref.html
@@ -0,0 +1,8 @@
+<DOCTYPE HTML>
+<html>
+<body>
+<div style="overflow:hidden; border:1px solid black;">
+  <div style="position:relative; left:50px; height:10px; background:blue;"></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/928607-1.html
@@ -0,0 +1,15 @@
+<DOCTYPE HTML>
+<html class="reftest-wait">
+<body>
+<div style="overflow:hidden; border:1px solid black;">
+  <div id="d" style="position:relative; left:10px; height:10px; background:blue;"></div>
+</div>
+<script>
+function doTest() {
+  d.style.left = "50px";
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1770,8 +1770,9 @@ test-pref(layout.css.flexbox.enabled,tru
 == 883987-1f.html 883987-1-ref.html
 == 890495-1.html 890495-1-ref.html
 == 894931-1.html 894931-1-ref.html
 == 897491-1.html 897491-1-ref.html
 == 897491-2.html 897491-2-ref.html
 fuzzy(1,10000) fuzzy-if(Android&&AndroidVersion>=15,5,10000) == 902330-1.html 902330-1-ref.html
 fuzzy-if(Android,8,400) == 906199-1.html 906199-1-ref.html
 == 921716-1.html 921716-1-ref.html
+fuzzy-if(cocoaWidget,1,40) == 928607-1.html 928607-1-ref.html
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -42,16 +42,17 @@
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsCSSParser.h"
 #include "mozilla/css/ImportRule.h"
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsCrossSiteListenerProxy.h"
+#include "nsINetworkSeer.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 
 #include "nsIMediaList.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsError.h"
@@ -1428,16 +1429,22 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
     nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
     rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
     if (NS_FAILED(rv)) {
       LOG_ERROR(("  Failed to create stream loader for sync load"));
       SheetComplete(aLoadData, rv);
       return rv;
     }
 
+    if (mDocument) {
+      mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
+                              nsINetworkSeer::LEARN_LOAD_SUBRESOURCE,
+                              mDocument);
+    }
+
     // Just load it
     nsCOMPtr<nsIInputStream> stream;
     nsCOMPtr<nsIChannel> channel;
     rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nullptr,
                     nullptr, nullptr, nsIRequest::LOAD_NORMAL,
                     getter_AddRefs(channel));
     if (NS_FAILED(rv)) {
       LOG_ERROR(("  Failed to open URI synchronously"));
@@ -1601,16 +1608,21 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
       SheetComplete(aLoadData, rv);
       return rv;
     }
     channelListener = corsListener;
   } else {
     channelListener = streamLoader;
   }
 
+  if (mDocument) {
+    mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
+                            nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument);
+  }
+
   rv = channel->AsyncOpen(channelListener, nullptr);
 
 #ifdef DEBUG
   mSyncCallback = false;
 #endif
 
   if (NS_FAILED(rv)) {
     LOG_ERROR(("  Failed to create stream loader"));
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -24,16 +24,17 @@
 #include "nsIScriptSecurityManager.h"
 
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIWebNavigation.h"
+#include "nsINetworkSeer.h"
 
 #include "nsIConsoleService.h"
 
 #include "nsStyleSet.h"
 #include "nsPrintfCString.h"
 #include "mozilla/gfx/2D.h"
 
 using namespace mozilla;
@@ -370,16 +371,20 @@ nsUserFontSet::StartLoad(gfxMixedFontFam
 #endif
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel)
     httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
   rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsIDocument *document = ps->GetDocument();
+  mozilla::net::SeerLearn(aFontFaceSrc->mURI, document->GetDocumentURI(),
+                          nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadGroup);
+
   bool inherits = false;
   rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
                            nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
                            &inherits);
   if (NS_SUCCEEDED(rv) && inherits) {
     // allow data, javascript, etc URI's
     rv = channel->AsyncOpen(streamLoader, nullptr);
   } else {
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -1,22 +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/. */
 
 #include "CSFLog.h"
 #include "nspr.h"
 
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#elif defined XP_WIN
+#include <winsock2.h>
+#endif
+
 #include "AudioConduit.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Services.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsThreadUtils.h"
+#ifdef MOZILLA_INTERNAL_API
+#include "Latency.h"
+#endif
 
 #include "webrtc/voice_engine/include/voe_errors.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #endif
 
 namespace mozilla {
@@ -199,16 +208,22 @@ MediaConduitErrorCode WebrtcAudioConduit
   }
 
   if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine)))
   {
     CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
 
+  if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine)))
+  {
+    CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__);
+    return kMediaConduitSessionNotInited;
+  }
+
   if (other) {
     mChannel = other->mChannel;
   } else {
     // init the engine with our audio device layer
     if(mPtrVoEBase->Init() == -1)
     {
       CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__);
       return kMediaConduitSessionNotInited;
@@ -505,16 +520,23 @@ WebrtcAudioConduit::SendAudioFrame(const
 
   // if transmission is not started .. conduit cannot insert frames
   if(!mEngineTransmitting)
   {
     CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
 
+#ifdef MOZILLA_INTERNAL_API
+    if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+      struct Processing insert = { TimeStamp::Now(), 0 };
+      mProcessing.AppendElement(insert);
+    }
+#endif
+
   capture_delay = mCaptureDelay;
   //Insert the samples
   if(mPtrVoEXmedia->ExternalRecordingInsertData(audio_data,
                                                 lengthSamples,
                                                 samplingFreqHz,
                                                 capture_delay) == -1)
   {
     int error = mPtrVoEBase->LastError();
@@ -583,29 +605,62 @@ WebrtcAudioConduit::GetAudioFrame(int16_
     CSFLogError(logTag,  "%s Getting audio data Failed %d", __FUNCTION__, error);
     if(error == VE_RUNTIME_PLAY_ERROR)
     {
       return kMediaConduitPlayoutError;
     }
     return kMediaConduitUnknownError;
   }
 
+#ifdef MOZILLA_INTERNAL_API
+  if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+    if (mProcessing.Length() > 0) {
+      unsigned int now;
+      mPtrVoEVideoSync->GetPlayoutTimestamp(mChannel, now);
+      if (static_cast<uint32_t>(now) != mLastTimestamp) {
+        mLastTimestamp = static_cast<uint32_t>(now);
+        // Find the block that includes this timestamp in the network input
+        while (mProcessing.Length() > 0) {
+          // FIX! assumes 20ms @ 48000Hz
+          // FIX handle wrap-around
+          if (mProcessing[0].mRTPTimeStamp + 20*(48000/1000) >= now) {
+            TimeDuration t = TimeStamp::Now() - mProcessing[0].mTimeStamp;
+            // Wrap-around?
+            int64_t delta = t.ToMilliseconds() + (now - mProcessing[0].mRTPTimeStamp)/(48000/1000);
+            LogTime(AsyncLatencyLogger::AudioRecvRTP, ((uint64_t) this), delta);
+            break;
+          }
+          mProcessing.RemoveElementAt(0);
+        }
+      }
+    }
+  }
+#endif
   CSFLogDebug(logTag,"%s GetAudioFrame:Got samples: length %d ",__FUNCTION__,
                                                                lengthSamples);
   return kMediaConduitNoError;
 }
 
 // Transport Layer Callbacks
 MediaConduitErrorCode
 WebrtcAudioConduit::ReceivedRTPPacket(const void *data, int len)
 {
   CSFLogDebug(logTag,  "%s : channel %d", __FUNCTION__, mChannel);
 
   if(mEngineReceiving)
   {
+#ifdef MOZILLA_INTERNAL_API
+    if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+      // timestamp is at 32 bits in ([1])
+      struct Processing insert = { TimeStamp::Now(),
+                                   ntohl(static_cast<const uint32_t *>(data)[1]) };
+      mProcessing.AppendElement(insert);
+    }
+#endif
+
     if(mPtrVoENetwork->ReceivedRTPPacket(mChannel,data,len) == -1)
     {
       int error = mPtrVoEBase->LastError();
       CSFLogError(logTag, "%s RTP Processing Error %d", __FUNCTION__, error);
       if(error == VE_RTP_RTCP_MODULE_ERROR)
       {
         return kMediaConduitRTPRTCPModuleError;
       }
@@ -654,16 +709,28 @@ int WebrtcAudioConduit::SendPacket(int c
     if (mOtherDirection)
     {
       return mOtherDirection->SendPacket(channel, data, len);
     }
     CSFLogDebug(logTag,  "%s : Asked to send RTP without an RTP sender on channel %d",
                 __FUNCTION__, channel);
     return -1;
   } else {
+#ifdef MOZILLA_INTERNAL_API
+    if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
+      if (mProcessing.Length() > 0) {
+        TimeStamp started = mProcessing[0].mTimeStamp;
+        mProcessing.RemoveElementAt(0);
+        mProcessing.RemoveElementAt(0); // 20ms packetization!  Could automate this by watching sizes
+        TimeDuration t = TimeStamp::Now() - started;
+        int64_t delta = t.ToMilliseconds();
+        LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta);
+      }
+    }
+#endif
     if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK))
     {
       CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__);
       return len;
     } else {
       CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__);
       return -1;
     }
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -2,35 +2,39 @@
  * 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 AUDIO_SESSION_H_
 #define AUDIO_SESSION_H_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
 
 #include "MediaConduitInterface.h"
 
 // Audio Engine Includes
 #include "webrtc/common_types.h"
 #include "webrtc/voice_engine/include/voe_base.h"
 #include "webrtc/voice_engine/include/voe_volume_control.h"
 #include "webrtc/voice_engine/include/voe_codec.h"
 #include "webrtc/voice_engine/include/voe_file.h"
 #include "webrtc/voice_engine/include/voe_network.h"
 #include "webrtc/voice_engine/include/voe_external_media.h"
 #include "webrtc/voice_engine/include/voe_audio_processing.h"
+#include "webrtc/voice_engine/include/voe_video_sync.h"
 
 //Some WebRTC types for short notations
  using webrtc::VoEBase;
  using webrtc::VoENetwork;
  using webrtc::VoECodec;
  using webrtc::VoEExternalMedia;
  using webrtc::VoEAudioProcessing;
+ using webrtc::VoEVideoSync;
 
 /** This file hosts several structures identifying different aspects
  * of a RTP Session.
  */
 
 namespace mozilla {
 
 /**
@@ -142,16 +146,17 @@ public:
 
   WebrtcAudioConduit():
                       mOtherDirection(nullptr),
                       mShutDown(false),
                       mVoiceEngine(nullptr),
                       mTransport(nullptr),
                       mEngineTransmitting(false),
                       mEngineReceiving(false),
+                      mLastTimestamp(0),
                       mChannel(-1),
                       mCurSendCodecConfig(nullptr),
                       mCaptureDelay(150),
                       mEchoOn(true),
                       mEchoCancel(webrtc::kEcAec)
   {
   }
 
@@ -204,22 +209,33 @@ private:
   // conduit to die
   webrtc::VoiceEngine* mVoiceEngine;
   mozilla::RefPtr<TransportInterface> mTransport;
   webrtc::VoENetwork*  mPtrVoENetwork;
   webrtc::VoEBase*     mPtrVoEBase;
   webrtc::VoECodec*    mPtrVoECodec;
   webrtc::VoEExternalMedia* mPtrVoEXmedia;
   webrtc::VoEAudioProcessing* mPtrVoEProcessing;
+  webrtc::VoEVideoSync* mPtrVoEVideoSync;
 
   //engine states of our interets
   bool mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up
   bool mEngineReceiving;    // If true => VoiceEngine Receive-subsystem is up
                             // and playout is enabled
 
+  // Keep track of each inserted RTP block and the time it was inserted
+  // so we can estimate the clock time for a specific TimeStamp coming out
+  // (for when we send data to MediaStreamTracks).  Blocks are aged out as needed.
+  struct Processing {
+    TimeStamp mTimeStamp;
+    uint32_t mRTPTimeStamp; // RTP timestamps received
+  };
+  nsAutoTArray<Processing,8> mProcessing;
+  uint32_t mLastTimestamp;
+
   int mChannel;
   RecvCodecList    mRecvCodecList;
   AudioCodecConfig* mCurSendCodecConfig;
 
   // Current "capture" delay (really output plus input delay)
   int32_t mCaptureDelay;
 
   bool mEchoOn;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -19,16 +19,20 @@
 #include "PeerConnectionCtx.h"
 #include "runnable_utils.h"
 #include "cpr_socket.h"
 #include "debug-psipcc-types.h"
 #include "prcvar.h"
 
 #include "mozilla/Telemetry.h"
 
+#ifdef MOZILLA_INTERNAL_API
+#include "mozilla/dom/RTCPeerConnectionBinding.h"
+#endif
+
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
 #include "StaticPtr.h"
 extern "C" {
 #include "../sipcc/core/common/thread_monitor.h"
 }
 
@@ -36,16 +40,67 @@ static const char* logTag = "PeerConnect
 
 extern "C" {
 extern PRCondVar *ccAppReadyToStartCond;
 extern PRLock *ccAppReadyToStartLock;
 extern char ccAppReadyToStart;
 }
 
 namespace mozilla {
+
+using namespace dom;
+
+// Convert constraints to C structures
+
+#ifdef MOZILLA_INTERNAL_API
+static void
+Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst,
+      bool mandatory = false) {
+  if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) {
+    aDst->was_passed = true;
+    aDst->value = aSrc.Value();
+    aDst->mandatory = mandatory;
+  }
+}
+#endif
+
+MediaConstraintsExternal::MediaConstraintsExternal() {
+  memset(&mConstraints, 0, sizeof(mConstraints));
+}
+
+MediaConstraintsExternal::MediaConstraintsExternal(
+    const MediaConstraintsInternal &aSrc) {
+  cc_media_constraints_t* c = &mConstraints;
+  memset(c, 0, sizeof(*c));
+#ifdef MOZILLA_INTERNAL_API
+  Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true);
+  Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true);
+  Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel,
+        true);
+  if (aSrc.mOptional.WasPassed()) {
+    const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value();
+    for (uint32_t i = 0; i < array.Length(); i++) {
+      Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio);
+      Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video);
+      Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel);
+    }
+  }
+#endif
+}
+
+cc_media_constraints_t*
+MediaConstraintsExternal::build() const {
+  cc_media_constraints_t* cc  = (cc_media_constraints_t*)
+    cpr_malloc(sizeof(cc_media_constraints_t));
+  if (cc) {
+    *cc = mConstraints;
+  }
+  return cc;
+}
+
 class PeerConnectionCtxShutdown : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
 
   PeerConnectionCtxShutdown() {}
 
   void Init()
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
@@ -12,37 +12,40 @@
 #include "CC_Device.h"
 #include "CC_DeviceInfo.h"
 #include "CC_Call.h"
 #include "CC_CallInfo.h"
 #include "CC_Line.h"
 #include "CC_LineInfo.h"
 #include "CC_Observer.h"
 #include "CC_FeatureInfo.h"
+#include "cpr_stdlib.h"
 
 #include "StaticPtr.h"
 #include "PeerConnectionImpl.h"
 
 namespace mozilla {
 class PeerConnectionCtxShutdown;
+
+// Unit-test helper, because cc_media_constraints_t is hard to forward-declare
+
+class MediaConstraintsExternal {
+public:
+  MediaConstraintsExternal();
+  MediaConstraintsExternal(const dom::MediaConstraintsInternal &aOther);
+  cc_media_constraints_t* build() const;
+protected:
+  cc_media_constraints_t mConstraints;
+};
 }
 
 namespace sipcc {
 
 using namespace mozilla;
 
-// Unit-test helper, because cc_media_constraints_t is hard to forward-declare
-
-class MediaConstraintsExternal {
-public:
-  MediaConstraintsExternal(cc_media_constraints_t *aConstraints)
-  : mConstraints(aConstraints) {}
-  cc_media_constraints_t *mConstraints;
-};
-
 class OnCallEventArgs {
 public:
   OnCallEventArgs(ccapi_call_event_e aCallEvent, CSF::CC_CallInfoPtr aInfo)
   : mCallEvent(aCallEvent), mInfo(aInfo) {}
 
   ccapi_call_event_e mCallEvent;
   CSF::CC_CallInfoPtr mInfo;
 };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -94,53 +94,16 @@ PRLogModuleInfo *signalingLogInfo() {
     logModuleInfo = PR_NewLogModule("signaling");
   }
   return logModuleInfo;
 }
 
 
 namespace sipcc {
 
-// Convert constraints to C structures
-
-#ifdef MOZILLA_INTERNAL_API
-static void
-Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst,
-      bool mandatory = false) {
-  if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) {
-    aDst->was_passed = true;
-    aDst->value = aSrc.Value();
-    aDst->mandatory = mandatory;
-  }
-}
-#endif
-
-static cc_media_constraints_t*
-ConvertConstraints(const MediaConstraintsInternal& aSrc) {
-  cc_media_constraints_t* c = (cc_media_constraints_t*)
-      cpr_malloc(sizeof(cc_media_constraints_t));
-  NS_ENSURE_TRUE(c,c);
-  memset(c, 0, sizeof(cc_media_constraints_t));
-#ifdef MOZILLA_INTERNAL_API
-  Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true);
-  Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true);
-  Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel,
-        true);
-  if (aSrc.mOptional.WasPassed()) {
-    const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value();
-    for (uint32_t i = 0; i < array.Length(); i++) {
-      Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio);
-      Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video);
-      Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel);
-    }
-  }
-#endif
-  return c;
-}
-
 // Getting exceptions back down from PCObserver is generally not harmful.
 namespace {
 class JSErrorResult : public ErrorResult
 {
 public:
   ~JSErrorResult()
   {
 #ifdef MOZILLA_INTERNAL_API
@@ -1049,51 +1012,53 @@ PeerConnectionImpl::NotifyDataChannel(al
                                pco),
                 NS_DISPATCH_NORMAL);
 #endif
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints)
 {
-  return CreateOffer(MediaConstraintsExternal(ConvertConstraints(aConstraints)));
+  return CreateOffer(MediaConstraintsExternal (aConstraints));
 }
 
 // Used by unit tests and the IDL CreateOffer.
 NS_IMETHODIMP
 PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Create Offer");
 
-  NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED);
-  mInternal->mCall->createOffer(aConstraints.mConstraints, tc);
+  cc_media_constraints_t* cc_constraints = aConstraints.build();
+  NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
+  mInternal->mCall->createOffer(cc_constraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints)
 {
-  return CreateAnswer(MediaConstraintsExternal(ConvertConstraints(aConstraints)));
+  return CreateAnswer(MediaConstraintsExternal (aConstraints));
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Create Answer");
 
-  NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED);
-  mInternal->mCall->createAnswer(aConstraints.mConstraints, tc);
+  cc_media_constraints_t* cc_constraints = aConstraints.build();
+  NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
+  mInternal->mCall->createAnswer(cc_constraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -69,16 +69,17 @@ class RTCStatsReportInternal;
 #ifdef USE_FAKE_PCOBSERVER
 typedef test::AFakePCObserver PeerConnectionObserver;
 typedef const char *PCObserverString;
 #else
 class PeerConnectionObserver;
 typedef NS_ConvertUTF8toUTF16 PCObserverString;
 #endif
 }
+class MediaConstraintsExternal;
 }
 
 #if defined(__cplusplus) && __cplusplus >= 201103L
 typedef struct Timecard Timecard;
 #else
 #include "timecard.h"
 #endif
 
@@ -93,28 +94,28 @@ void func (__VA_ARGS__, rv)
 NS_IMETHODIMP func(__VA_ARGS__, resulttype **result);                  \
 already_AddRefed<resulttype> func (__VA_ARGS__, rv)
 
 namespace sipcc {
 
 using mozilla::dom::PeerConnectionObserver;
 using mozilla::dom::RTCConfiguration;
 using mozilla::dom::MediaConstraintsInternal;
+using mozilla::MediaConstraintsExternal;
 using mozilla::DOMMediaStream;
 using mozilla::NrIceCtx;
 using mozilla::NrIceMediaStream;
 using mozilla::DtlsIdentity;
 using mozilla::ErrorResult;
 using mozilla::NrIceStunServer;
 using mozilla::NrIceTurnServer;
 
 class PeerConnectionWrapper;
 class PeerConnectionMedia;
 class RemoteSourceStreamInfo;
-class MediaConstraintsExternal;
 class OnCallEventArgs;
 
 class IceConfiguration
 {
 public:
   bool addStunServer(const std::string& addr, uint16_t port)
   {
     NrIceStunServer* server(NrIceStunServer::Create(addr, port));
--- a/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
+++ b/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
@@ -5,16 +5,19 @@
 #ifndef FAKE_MEDIA_STREAMIMPL_H_
 #define FAKE_MEDIA_STREAMIMPL_H_
 
 #include "FakeMediaStreams.h"
 
 #include "nspr.h"
 #include "nsError.h"
 
+void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t b, int64_t c) {}
+void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t b, int64_t c) {}
+
 static const int AUDIO_BUFFER_SIZE = 1600;
 static const int NUM_CHANNELS      = 2;
 
 NS_IMPL_ISUPPORTS1(Fake_DOMMediaStream, nsIDOMMediaStream)
 
 // Fake_SourceMediaStream
 nsresult Fake_SourceMediaStream::Start() {
   mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -73,40 +73,32 @@ uint16_t kBogusSrflxPort(1001);
 
 namespace sipcc {
 
 // We can't use mozilla/dom/MediaConstraintsBinding.h here because it uses
 // nsString, so we pass constraints in using MediaConstraintsExternal instead
 
 class MediaConstraints : public MediaConstraintsExternal {
 public:
-  MediaConstraints()
-  : MediaConstraintsExternal((cc_media_constraints_t*)
-                             cpr_malloc(sizeof(*mConstraints))) {
-    MOZ_ASSERT(mConstraints);
-    memset(mConstraints, 0, sizeof(*mConstraints));
-  }
-
   void setBooleanConstraint(const char *namePtr, bool value, bool mandatory) {
     cc_boolean_constraint_t &member (getMember(namePtr));
     member.was_passed = true;
     member.value = value;
     member.mandatory = mandatory;
   }
 private:
   cc_boolean_constraint_t &getMember(const char *namePtr) {
-    MOZ_ASSERT(mConstraints);
     if (strcmp(namePtr, "OfferToReceiveAudio") == 0) {
-        return mConstraints->offer_to_receive_audio;
+        return mConstraints.offer_to_receive_audio;
     }
     if (strcmp(namePtr, "OfferToReceiveVideo") == 0) {
-        return mConstraints->offer_to_receive_video;
+        return mConstraints.offer_to_receive_video;
     }
     MOZ_ASSERT(false);
-    return mConstraints->moz_dont_offer_datachannel;
+    return mConstraints.moz_dont_offer_datachannel;
   }
 };
 }
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace test {
--- a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
@@ -39,16 +39,22 @@
         'audio_device_utility.h',
         'audio_device_impl.cc',
         'audio_device_impl.h',
         'audio_device_config.h',
         'dummy/audio_device_dummy.h',
         'dummy/audio_device_utility_dummy.h',
       ],
       'conditions': [
+        ['build_with_mozilla==1', {
+          'include_dirs': [
+            '$(DIST)/include',
+            '$(DIST)/include/nspr',
+          ],
+        }],
         ['OS=="linux" or include_alsa_audio==1 or include_pulse_audio==1', {
           'include_dirs': [
             'linux',
           ],
         }], # OS=="linux" or include_alsa_audio==1 or include_pulse_audio==1
         ['OS=="ios"', {
           'include_dirs': [
             'ios',
--- a/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc
@@ -14,16 +14,23 @@
 #include "audio_device_alsa_linux.h"
 #include "audio_device_config.h"
 
 #include "event_wrapper.h"
 #include "system_wrappers/interface/sleep.h"
 #include "trace.h"
 #include "thread_wrapper.h"
 
+#include "Latency.h"
+
+#define LOG_FIRST_CAPTURE(x) LogTime(AsyncLatencyLogger::AudioCaptureBase, \
+                                     reinterpret_cast<uint64_t>(x), 0)
+#define LOG_CAPTURE_FRAMES(x, frames) LogLatency(AsyncLatencyLogger::AudioCapture, \
+                                                 reinterpret_cast<uint64_t>(x), frames)
+
 webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable;
 
 // Accesses ALSA functions through our late-binding symbol table instead of
 // directly. This way we don't have to link to libasound, which means our binary
 // will work on systems that don't have it.
 #define LATE(sym) \
   LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym)
 
@@ -91,16 +98,17 @@ AudioDeviceLinuxALSA::AudioDeviceLinuxAL
     _playChannels(ALSA_PLAYOUT_CH),
     _recordingBuffer(NULL),
     _playoutBuffer(NULL),
     _recordingFramesLeft(0),
     _playoutFramesLeft(0),
     _playBufType(AudioDeviceModule::kFixedBufferSize),
     _initialized(false),
     _recording(false),
+    _firstRecord(true),
     _playing(false),
     _recIsInitialized(false),
     _playIsInitialized(false),
     _AGC(false),
     _recordingDelay(0),
     _playoutDelay(0),
     _playWarning(0),
     _playError(0),
@@ -1444,16 +1452,17 @@ int32_t AudioDeviceLinuxALSA::StartRecor
     {
         WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
                      "   failed to alloc recording buffer");
         _recording = false;
         return -1;
     }
     // RECORDING
     const char* threadName = "webrtc_audio_module_capture_thread";
+    _firstRecord = true;
     _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc,
                                                 this,
                                                 kRealtimePriority,
                                                 threadName);
     if (_ptrThreadRec == NULL)
     {
         WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
                      "  failed to create the rec audio thread");
@@ -2262,16 +2271,21 @@ bool AudioDeviceLinuxALSA::RecThreadProc
         memcpy(&_recordingBuffer[_recordingBufferSizeIn10MS - left_size],
                buffer, size);
         _recordingFramesLeft -= frames;
 
         if (!_recordingFramesLeft)
         { // buf is full
             _recordingFramesLeft = _recordingFramesIn10MS;
 
+            if (_firstRecord) {
+              LOG_FIRST_CAPTURE(this);
+              _firstRecord = false;
+            }
+            LOG_CAPTURE_FRAMES(this, _recordingFramesIn10MS);
             // store the recorded buffer (no action will be taken if the
             // #recorded samples is not a full buffer)
             _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer,
                                                _recordingFramesIn10MS);
 
             uint32_t currentMicLevel = 0;
             uint32_t newMicLevel = 0;
 
--- a/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h
@@ -229,16 +229,17 @@ private:
     uint32_t _recordingFramesLeft;
     uint32_t _playoutFramesLeft;
 
     AudioDeviceModule::BufferType _playBufType;
 
 private:
     bool _initialized;
     bool _recording;
+    bool _firstRecord;
     bool _playing;
     bool _recIsInitialized;
     bool _playIsInitialized;
     bool _AGC;
 
     snd_pcm_sframes_t _recordingDelay;
     snd_pcm_sframes_t _playoutDelay;
 
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
@@ -104,17 +104,27 @@ const float WebRtcAec_overDriveCurve[65]
     1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f,
     1.9186f, 1.9270f, 1.9354f, 1.9437f, 1.9520f, 1.9601f,
     1.9682f, 1.9763f, 1.9843f, 1.9922f, 2.0000f
 };
 
 // Target suppression levels for nlp modes.
 // log{0.001, 0.00001, 0.00000001}
 static const float kTargetSupp[3] = { -6.9f, -11.5f, -18.4f };
-static const float kMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
+
+// Two sets of parameters, one for the extended filter mode.
+static const float kExtendedMinOverDrive[3] = { 3.0f, 6.0f, 15.0f };
+static const float kNormalMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
+static const float kExtendedSmoothingCoefficients[2][2] =
+    { { 0.9f, 0.1f }, { 0.92f, 0.08f } };
+static const float kNormalSmoothingCoefficients[2][2] =
+    { { 0.9f, 0.1f }, { 0.93f, 0.07f } };
+
+// Number of partitions forming the NLP's "preferred" bands.
+enum { kPrefBandSize = 24 };
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 extern int webrtc_aec_instance_count;
 #endif
 
 // "Private" function prototypes.
 static void ProcessBlock(AecCore* aec);
 
@@ -276,89 +286,92 @@ int WebRtcAec_FreeAec(AecCore* aec)
 
     free(aec);
     return 0;
 }
 
 static void FilterFar(AecCore* aec, float yf[2][PART_LEN1])
 {
   int i;
-  for (i = 0; i < NR_PART; i++) {
+  for (i = 0; i < aec->num_partitions; i++) {
     int j;
     int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
     int pos = i * PART_LEN1;
     // Check for wrap
-    if (i + aec->xfBufBlockPos >= NR_PART) {
-      xPos -= NR_PART*(PART_LEN1);
+    if (i + aec->xfBufBlockPos >= aec->num_partitions) {
+      xPos -= aec->num_partitions*(PART_LEN1);
     }
 
     for (j = 0; j < PART_LEN1; j++) {
       yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
                         aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
       yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
                         aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
     }
   }
 }
 
 static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1])
 {
+  const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu;
+  const float error_threshold = aec->extended_filter_enabled ?
+      kExtendedErrorThreshold : aec->normal_error_threshold;
   int i;
-  float absEf;
+  float abs_ef;
   for (i = 0; i < (PART_LEN1); i++) {
     ef[0][i] /= (aec->xPow[i] + 1e-10f);
     ef[1][i] /= (aec->xPow[i] + 1e-10f);
-    absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
+    abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
 
-    if (absEf > aec->errThresh) {
-      absEf = aec->errThresh / (absEf + 1e-10f);
-      ef[0][i] *= absEf;
-      ef[1][i] *= absEf;
+    if (abs_ef > error_threshold) {
+      abs_ef = error_threshold / (abs_ef + 1e-10f);
+      ef[0][i] *= abs_ef;
+      ef[1][i] *= abs_ef;
     }
 
     // Stepsize factor
-    ef[0][i] *= aec->mu;
-    ef[1][i] *= aec->mu;
+    ef[0][i] *= mu;
+    ef[1][i] *= mu;
   }
 }
 
 // Time-unconstrined filter adaptation.
 // TODO(andrew): consider for a low-complexity mode.
 //static void FilterAdaptationUnconstrained(AecCore* aec, float *fft,
 //                                          float ef[2][PART_LEN1]) {
 //  int i, j;
-//  for (i = 0; i < NR_PART; i++) {
+//  for (i = 0; i < aec->num_partitions; i++) {
 //    int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
 //    int pos;
 //    // Check for wrap
-//    if (i + aec->xfBufBlockPos >= NR_PART) {
-//      xPos -= NR_PART * PART_LEN1;
+//    if (i + aec->xfBufBlockPos >= aec->num_partitions) {
+//      xPos -= aec->num_partitions * PART_LEN1;
 //    }
 //
 //    pos = i * PART_LEN1;
 //
 //    for (j = 0; j < PART_LEN1; j++) {
-//      aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0],
-//                                      -aec->xfBuf[xPos + j][1],
-//                                      ef[j][0], ef[j][1]);
-//      aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0],
-//                                      -aec->xfBuf[xPos + j][1],
-//