merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 18 Dec 2014 13:50:27 +0100
changeset 246156 9bb8b0b4daae5f534c5d809a5a502375571474ae
parent 246097 088baba391f723981976db466690e57cefc6034a (current diff)
parent 246155 8b71a52d7730a894949a2f7d371483cc7cb90bed (diff)
child 246169 32d360316ff37696480d4ca324df7f8a833c2c09
child 246193 aa3ff71c8b4d4465b8f64323a500cb5a654d4667
child 246218 f2bfb9716f5cb2880fdae579cfc10cf40e52a63a
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
python/mozbuild/mozbuild/mach_commands.py
--- a/accessible/atk/moz.build
+++ b/accessible/atk/moz.build
@@ -46,8 +46,10 @@ FINAL_LIBRARY = 'xul'
 if CONFIG['MOZ_ENABLE_GTK']:
     CFLAGS += CONFIG['TK_CFLAGS']
     CXXFLAGS += CONFIG['TK_CFLAGS']
 
 if CONFIG['MOZ_ENABLE_DBUS']:
     CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
+
+FAIL_ON_WARNINGS = True
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -24,8 +24,10 @@ if CONFIG['ACCESSIBILITY']:
     LOCAL_INCLUDES += [
         '../base',
         '../generic',
     ]
 
     FINAL_LIBRARY = 'xul'
 
 include('/ipc/chromium/chromium-config.mozbuild')
+
+FAIL_ON_WARNINGS = True
--- a/accessible/other/moz.build
+++ b/accessible/other/moz.build
@@ -17,8 +17,10 @@ SOURCES += [
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/xul',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/accessible/windows/ia2/moz.build
+++ b/accessible/windows/ia2/moz.build
@@ -45,8 +45,10 @@ LOCAL_INCLUDES += [
 ]
 
 FINAL_LIBRARY = 'xul'
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
+
+FAIL_ON_WARNINGS = True
--- a/accessible/windows/msaa/moz.build
+++ b/accessible/windows/msaa/moz.build
@@ -54,8 +54,10 @@ LOCAL_INCLUDES += [
     '/accessible/xpcom',
     '/accessible/xul',
     '/dom/base',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/accessible/windows/sdn/moz.build
+++ b/accessible/windows/sdn/moz.build
@@ -19,8 +19,10 @@ LOCAL_INCLUDES += [
     '/accessible/xul',
 ]
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 DEFINES['NOMINMAX'] = True
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/accessible/windows/uia/moz.build
+++ b/accessible/windows/uia/moz.build
@@ -18,8 +18,10 @@ LOCAL_INCLUDES += [
 ]
 
 FINAL_LIBRARY = 'xul'
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
+
+FAIL_ON_WARNINGS = True
--- a/accessible/xpcom/moz.build
+++ b/accessible/xpcom/moz.build
@@ -42,8 +42,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
         '/accessible/mac',
     ]
 else:
     LOCAL_INCLUDES += [
         '/accessible/other',
     ]
 
 FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+    FAIL_ON_WARNINGS = True
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1068,8 +1068,13 @@ pref("dom.mozSettings.SettingsDB.debug.e
 pref("dom.mozSettings.SettingsManager.debug.enabled", true);
 pref("dom.mozSettings.SettingsRequestManager.debug.enabled", true);
 pref("dom.mozSettings.SettingsService.debug.enabled", true);
 
 pref("dom.mozSettings.SettingsDB.verbose.enabled", false);
 pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
 pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
 pref("dom.mozSettings.SettingsService.verbose.enabled", false);
+
+// Controlling whether we want to allow forcing some Settings
+// IndexedDB transactions to be opened as readonly or keep everything as
+// readwrite.
+pref("dom.mozSettings.allowForceReadOnly", false);
--- a/b2g/app/moz.build
+++ b/b2g/app/moz.build
@@ -69,8 +69,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     ]
 
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'version',
     ]
+
+FAIL_ON_WARNINGS = True
--- a/b2g/gaia/moz.build
+++ b/b2g/gaia/moz.build
@@ -13,8 +13,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['B2G_NAME'] = 'L"%s-bin%s"' % (PROGRAM, CONFIG['BIN_SUFFIX'])
     DEFINES['GAIA_PATH'] = 'L"gaia\\\\profile"'
 else:
     SOURCES += [
         'run-b2g.c',
     ]
     DEFINES['B2G_NAME'] = '"%s-bin%s"' % (PROGRAM, CONFIG['BIN_SUFFIX'])
     DEFINES['GAIA_PATH'] = '"gaia/profile"'
+
+FAIL_ON_WARNINGS = True
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -67,8 +67,10 @@ DISABLE_STL_WRAPPING = True
 
 if CONFIG['MOZ_LINKER']:
     OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
 if CONFIG['HAVE_CLOCK_MONOTONIC']:
     OS_LIBS += CONFIG['REALTIME_LIBS']
 
 JAR_MANIFESTS += ['jar.mn']
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/about/moz.build
+++ b/browser/components/about/moz.build
@@ -12,8 +12,10 @@ SOURCES += [
     'AboutRedirector.cpp',
 ]
 
 FINAL_LIBRARY = 'browsercomps'
 
 LOCAL_INCLUDES += [
     '../build',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/build/moz.build
+++ b/browser/components/build/moz.build
@@ -29,8 +29,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'shlwapi',
         'version',
     ]
 
 # Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
 # GTK2: Need to link with glib for GNOME shell service
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'):
     OS_LIBS += CONFIG['TK_LIBS']
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/dirprovider/moz.build
+++ b/browser/components/dirprovider/moz.build
@@ -16,8 +16,10 @@ XPCSHELL_TESTS_MANIFESTS += [
     'tests/unit/xpcshell.ini',
 ]
 
 FINAL_LIBRARY = 'browsercomps'
 
 LOCAL_INCLUDES += [
     '../build'
 ]
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/feeds/moz.build
+++ b/browser/components/feeds/moz.build
@@ -34,8 +34,10 @@ EXTRA_PP_COMPONENTS += [
 FINAL_LIBRARY = 'browsercomps'
 
 for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'):
     DEFINES[var] = CONFIG[var]
 
 LOCAL_INCLUDES += [
     '../build',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/migration/moz.build
+++ b/browser/components/migration/moz.build
@@ -47,8 +47,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     ]
     DEFINES['HAS_SAFARI_MIGRATOR'] = True
 
 EXTRA_PP_JS_MODULES += [
     'MigrationUtils.jsm',
 ]
 
 FINAL_LIBRARY = 'browsercomps'
+
+FAIL_ON_WARNINGS = True
--- a/browser/components/shell/moz.build
+++ b/browser/components/shell/moz.build
@@ -44,8 +44,10 @@ EXTRA_COMPONENTS += [
     'nsSetDefaultBrowser.js',
     'nsSetDefaultBrowser.manifest',
 ]
 
 for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'):
     DEFINES[var] = '"%s"' % CONFIG[var]
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
+
+FAIL_ON_WARNINGS = True
--- a/docshell/build/moz.build
+++ b/docshell/build/moz.build
@@ -19,8 +19,10 @@ LOCAL_INCLUDES += [
     '/uriloader/exthandler',
     '/uriloader/prefetch',
     ]
 
 if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
     LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/dom/animation/AnimationPlayer.cpp
+++ b/dom/animation/AnimationPlayer.cpp
@@ -6,24 +6,29 @@
 #include "AnimationPlayer.h"
 #include "AnimationUtils.h"
 #include "mozilla/dom/AnimationPlayerBinding.h"
 #include "AnimationCommon.h" // For AnimationPlayerCollection,
                              // CommonAnimationManager
 #include "nsIDocument.h" // For nsIDocument
 #include "nsIPresShell.h" // For nsIPresShell
 #include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
+#include "PendingPlayerTracker.h" // For PendingPlayerTracker
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline,
+                                      mSource, mReady)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationPlayer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationPlayer)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationPlayer)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
 JSObject*
 AnimationPlayer::WrapObject(JSContext* aCx)
 {
   return dom::AnimationPlayerBinding::Wrap(aCx, this);
 }
 
 Nullable<TimeDuration>
@@ -42,32 +47,56 @@ AnimationPlayer::GetCurrentTime() const
     }
   }
   return result;
 }
 
 AnimationPlayState
 AnimationPlayer::PlayState() const
 {
+  if (mIsPending) {
+    return AnimationPlayState::Pending;
+  }
+
   Nullable<TimeDuration> currentTime = GetCurrentTime();
   if (currentTime.IsNull()) {
     return AnimationPlayState::Idle;
   }
 
   if (mStartTime.IsNull()) {
     return AnimationPlayState::Paused;
   }
 
   if (currentTime.Value() >= SourceContentEnd()) {
     return AnimationPlayState::Finished;
   }
 
   return AnimationPlayState::Running;
 }
 
+Promise*
+AnimationPlayer::GetReady(ErrorResult& aRv)
+{
+  // Lazily create the ready promise if it doesn't exist
+  if (!mReady) {
+    nsIGlobalObject* global = mTimeline->GetParentObject();
+    if (global) {
+      mReady = Promise::Create(global, aRv);
+      if (mReady && PlayState() != AnimationPlayState::Pending) {
+        mReady->MaybeResolve(this);
+      }
+    }
+  }
+  if (!mReady) {
+    aRv.Throw(NS_ERROR_FAILURE);
+  }
+
+  return mReady;
+}
+
 void
 AnimationPlayer::Play()
 {
   DoPlay();
   PostUpdate();
 }
 
 void
@@ -105,31 +134,49 @@ void
 AnimationPlayer::Tick()
 {
   if (mSource) {
     mSource->SetParentTime(GetCurrentTime());
   }
 }
 
 void
-AnimationPlayer::ResolveStartTime()
+AnimationPlayer::StartNow()
 {
   // Currently we only expect this method to be called when we are in the
   // middle of initiating/resuming playback so we should have an unresolved
   // start time to update and a fixed current time to seek to.
   MOZ_ASSERT(mStartTime.IsNull() && !mHoldTime.IsNull(),
              "Resolving the start time but we don't appear to be waiting"
              " to begin playback");
 
   Nullable<TimeDuration> readyTime = mTimeline->GetCurrentTime();
   // Bug 1096776: Once we support disappearing or inactive timelines we
   // will need special handling here.
   MOZ_ASSERT(!readyTime.IsNull(), "Missing or inactive timeline");
   mStartTime.SetValue(readyTime.Value() - mHoldTime.Value());
   mHoldTime.SetNull();
+
+  if (mReady) {
+    mReady->MaybeResolve(this);
+  }
+}
+
+void
+AnimationPlayer::Cancel()
+{
+  if (mIsPending) {
+    CancelPendingPlay();
+    if (mReady) {
+      mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    }
+  }
+
+  mHoldTime.SetNull();
+  mStartTime.SetNull();
 }
 
 bool
 AnimationPlayer::IsRunning() const
 {
   if (IsPaused() || !GetSource() || GetSource()->IsFinishedTransition()) {
     return false;
   }
@@ -168,17 +215,18 @@ AnimationPlayer::ComposeStyle(nsRefPtr<c
                               nsCSSPropertySet& aSetProperties,
                               bool& aNeedsRefreshes)
 {
   if (!mSource || mSource->IsFinishedTransition()) {
     return;
   }
 
   AnimationPlayState playState = PlayState();
-  if (playState == AnimationPlayState::Running) {
+  if (playState == AnimationPlayState::Running ||
+      playState == AnimationPlayState::Pending) {
     aNeedsRefreshes = true;
   }
 
   mSource->ComposeStyle(aStyleRule, aSetProperties);
 
   mIsPreviousStateFinished = (playState == AnimationPlayState::Finished);
 }
 
@@ -192,31 +240,42 @@ AnimationPlayer::DoPlay()
   Nullable<TimeDuration> currentTime = GetCurrentTime();
   if (currentTime.IsNull()) {
     mHoldTime.SetValue(TimeDuration(0));
   } else if (mHoldTime.IsNull()) {
     // If the hold time is null, we are already playing normally
     return;
   }
 
-  ResolveStartTime();
+  // Clear ready promise. We'll create a new one lazily.
+  mReady = nullptr;
+
+  StartNow();
 }
 
 void
 AnimationPlayer::DoPause()
 {
-  if (IsPaused()) {
-    return;
+  if (mIsPending) {
+    CancelPendingPlay();
+    // Resolve the ready promise since we currently only use it for
+    // players that are waiting to play. Later (in bug 1109390), we will
+    // use this for players waiting to pause as well and then we won't
+    // want to resolve it just yet.
+    if (mReady) {
+      mReady->MaybeResolve(this);
+    }
   }
+
   // Mark this as no longer running on the compositor so that next time
   // we update animations we won't throttle them and will have a chance
   // to remove the animation from any layer it might be on.
   mIsRunningOnCompositor = false;
 
-  // Bug 927349 - check for null result here and go to pending state
+  // Bug 1109390 - check for null result here and go to pending state
   mHoldTime = GetCurrentTime();
   mStartTime.SetNull();
 }
 
 void
 AnimationPlayer::FlushStyle() const
 {
   nsIDocument* doc = GetRenderedDocument();
@@ -229,16 +288,34 @@ void
 AnimationPlayer::PostUpdate()
 {
   AnimationPlayerCollection* collection = GetCollection();
   if (collection) {
     collection->NotifyPlayerUpdated();
   }
 }
 
+void
+AnimationPlayer::CancelPendingPlay()
+{
+  if (!mIsPending) {
+    return;
+  }
+
+  nsIDocument* doc = GetRenderedDocument();
+  if (doc) {
+    PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
+    if (tracker) {
+      tracker->RemovePlayPending(*this);
+    }
+  }
+
+  mIsPending = false;
+}
+
 StickyTimeDuration
 AnimationPlayer::SourceContentEnd() const
 {
   if (!mSource) {
     return StickyTimeDuration(0);
   }
 
   return mSource->Timing().mDelay
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -8,16 +8,17 @@
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
 #include "mozilla/dom/Animation.h" // for Animation
 #include "mozilla/dom/AnimationPlayerBinding.h" // for AnimationPlayState
 #include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
+#include "mozilla/dom/Promise.h" // for Promise
 #include "nsCSSProperty.h" // for nsCSSProperty
 
 // X11 has a #define for CurrentTime.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 struct JSContext;
@@ -32,44 +33,47 @@ class AnimValuesStyleRule;
 class CommonAnimationManager;
 } // namespace css
 
 class CSSAnimationPlayer;
 class CSSTransitionPlayer;
 
 namespace dom {
 
-class AnimationPlayer : public nsWrapperCache
+class AnimationPlayer : public nsISupports,
+                        public nsWrapperCache
 {
 protected:
   virtual ~AnimationPlayer() { }
 
 public:
   explicit AnimationPlayer(AnimationTimeline* aTimeline)
     : mTimeline(aTimeline)
+    , mIsPending(false)
     , mIsRunningOnCompositor(false)
     , mIsPreviousStateFinished(false)
   {
   }
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationPlayer)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationPlayer)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationPlayer)
 
   AnimationTimeline* GetParentObject() const { return mTimeline; }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual CSSAnimationPlayer* AsCSSAnimationPlayer() { return nullptr; }
   virtual CSSTransitionPlayer* AsCSSTransitionPlayer() { return nullptr; }
 
   // AnimationPlayer methods
   Animation* GetSource() const { return mSource; }
   AnimationTimeline* Timeline() const { return mTimeline; }
   Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
   Nullable<TimeDuration> GetCurrentTime() const;
   AnimationPlayState PlayState() const;
+  virtual Promise* GetReady(ErrorResult& aRv);
   virtual void Play();
   virtual void Pause();
   bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
 
   // Wrapper functions for AnimationPlayer DOM methods when called
   // from script. We often use the same methods internally and from
   // script but when called from script we (or one of our subclasses) perform
   // extra steps such as flushing style or converting the return type.
@@ -79,20 +83,21 @@ public:
   virtual void PlayFromJS() { Play(); }
   // PauseFromJS is currently only here for symmetry with PlayFromJS but
   // in future we will likely have to flush style in
   // CSSAnimationPlayer::PauseFromJS so we leave it for now.
   void PauseFromJS() { Pause(); }
 
   void SetSource(Animation* aSource);
   void Tick();
-  // Sets the start time of the player to the current time of its timeline.
-  // This should only be called on a player that is currently waiting to play
-  // (and therefore has a null start time but a fixed hold time).
-  void ResolveStartTime();
+
+  // Sets the start time of a player that is waiting to play to the current
+  // time of its timeline.
+  void StartNow();
+  void Cancel();
 
   const nsString& Name() const {
     return mSource ? mSource->Name() : EmptyString();
   }
 
   bool IsPaused() const { return PlayState() == AnimationPlayState::Paused; }
   bool IsRunning() const;
 
@@ -123,28 +128,44 @@ public:
                     bool& aNeedsRefreshes);
 
 protected:
   void DoPlay();
   void DoPause();
 
   void FlushStyle() const;
   void PostUpdate();
+  // Remove this player from the pending player tracker and resets mIsPending
+  // as necessary. The caller is responsible for resolving or aborting the
+  // mReady promise as necessary.
+  void CancelPendingPlay();
   StickyTimeDuration SourceContentEnd() const;
 
   nsIDocument* GetRenderedDocument() const;
   nsPresContext* GetPresContext() const;
   virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
   AnimationPlayerCollection* GetCollection() const;
 
   nsRefPtr<AnimationTimeline> mTimeline;
   nsRefPtr<Animation> mSource;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Player timescale
+
+  // A Promise that is replaced on each call to Play() (and in future Pause())
+  // and fulfilled when Play() is successfully completed.
+  // This object is lazily created by GetReady.
+  nsRefPtr<Promise> mReady;
+
+  // Indicates if the player is in the pending state. We use this rather
+  // than checking if this player is tracked by a PendingPlayerTracker.
+  // This is because the PendingPlayerTracker is associated with the source
+  // content's document but we need to know if we're pending even if the
+  // source content loses association with its document.
+  bool mIsPending;
   bool mIsRunningOnCompositor;
   // Indicates whether we were in the finished state during our
   // most recent unthrottled sample (our last ComposeStyle call).
   // FIXME: When we implement the finished promise (bug 1074630) we can
   // probably remove this and check if the promise has been settled yet
   // or not instead.
   bool mIsPreviousStateFinished; // Spec calls this "previous finished state"
 };
--- a/dom/animation/AnimationTimeline.cpp
+++ b/dom/animation/AnimationTimeline.cpp
@@ -50,27 +50,22 @@ AnimationTimeline::GetCurrentTimeStamp()
   if (result.IsNull()) {
     nsRefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
     if (!timing) {
       return result;
     }
     result = timing->GetNavigationStartTimeStamp();
   }
 
-  nsIPresShell* presShell = mDocument->GetShell();
-  if (MOZ_UNLIKELY(!presShell)) {
+  nsRefreshDriver* refreshDriver = GetRefreshDriver();
+  if (!refreshDriver) {
     return result;
   }
 
-  nsPresContext* presContext = presShell->GetPresContext();
-  if (MOZ_UNLIKELY(!presContext)) {
-    return result;
-  }
-
-  result = presContext->RefreshDriver()->MostRecentRefresh();
+  result = refreshDriver->MostRecentRefresh();
   // FIXME: We would like to assert that:
   //   mLastCurrentTime.IsNull() || result >= mLastCurrentTime
   // but due to bug 1043078 this will not be the case when the refresh driver
   // is restored from test control.
   mLastCurrentTime = result;
   return result;
 }
 
@@ -99,10 +94,26 @@ AnimationTimeline::ToTimeStamp(const Tim
   if (MOZ_UNLIKELY(!timing)) {
     return result;
   }
 
   result = timing->GetNavigationStartTimeStamp() + aTimeDuration;
   return result;
 }
 
+nsRefreshDriver*
+AnimationTimeline::GetRefreshDriver() const
+{
+  nsIPresShell* presShell = mDocument->GetShell();
+  if (MOZ_UNLIKELY(!presShell)) {
+    return nullptr;
+  }
+
+  nsPresContext* presContext = presShell->GetPresContext();
+  if (MOZ_UNLIKELY(!presContext)) {
+    return nullptr;
+  }
+
+  return presContext->RefreshDriver();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/AnimationTimeline.h
+++ b/dom/animation/AnimationTimeline.h
@@ -9,48 +9,55 @@
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
 #include "js/TypeDecls.h"
 #include "nsIDocument.h"
 
 struct JSContext;
+class nsRefreshDriver;
 
 namespace mozilla {
 namespace dom {
 
 class AnimationTimeline MOZ_FINAL : public nsWrapperCache
 {
 public:
   explicit AnimationTimeline(nsIDocument* aDocument)
     : mDocument(aDocument)
   {
   }
 
+protected:
+  virtual ~AnimationTimeline() { }
+
+public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationTimeline)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationTimeline)
 
-  nsISupports* GetParentObject() const { return mDocument; }
+  nsIGlobalObject* GetParentObject() const
+  {
+    return mDocument->GetParentObject();
+  }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // AnimationTimeline methods
   Nullable<TimeDuration> GetCurrentTime() const;
 
   // Wrapper functions for AnimationTimeline DOM methods when called from
   // script.
   Nullable<double> GetCurrentTimeAsDouble() const;
 
   Nullable<TimeDuration> ToTimelineTime(const TimeStamp& aTimeStamp) const;
   TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const;
 
 protected:
   TimeStamp GetCurrentTimeStamp() const;
-
-  virtual ~AnimationTimeline() { }
+  nsRefreshDriver* GetRefreshDriver() const;
 
   nsCOMPtr<nsIDocument> mDocument;
 
   // Store the most recently returned value of current time. This is used
   // in cases where we don't have a refresh driver (e.g. because we are in
   // a display:none iframe).
   mutable TimeStamp mLastCurrentTime;
 };
new file mode 100644
--- /dev/null
+++ b/dom/animation/PendingPlayerTracker.cpp
@@ -0,0 +1,35 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* 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 "PendingPlayerTracker.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+
+NS_IMPL_CYCLE_COLLECTION(PendingPlayerTracker, mPlayPendingSet)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingPlayerTracker, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingPlayerTracker, Release)
+
+void
+PendingPlayerTracker::AddPlayPending(dom::AnimationPlayer& aPlayer)
+{
+  mPlayPendingSet.PutEntry(&aPlayer);
+}
+
+void
+PendingPlayerTracker::RemovePlayPending(dom::AnimationPlayer& aPlayer)
+{
+  mPlayPendingSet.RemoveEntry(&aPlayer);
+}
+
+bool
+PendingPlayerTracker::IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const
+{
+  return mPlayPendingSet.Contains(const_cast<dom::AnimationPlayer*>(&aPlayer));
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/animation/PendingPlayerTracker.h
@@ -0,0 +1,36 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* 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_dom_PendingPlayerTracker_h
+#define mozilla_dom_PendingPlayerTracker_h
+
+#include "mozilla/dom/AnimationPlayer.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+
+class PendingPlayerTracker MOZ_FINAL
+{
+public:
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingPlayerTracker)
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingPlayerTracker)
+
+  void AddPlayPending(dom::AnimationPlayer& aPlayer);
+  void RemovePlayPending(dom::AnimationPlayer& aPlayer);
+  bool IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const;
+
+private:
+  ~PendingPlayerTracker() { }
+
+  typedef nsTHashtable<nsRefPtrHashKey<dom::AnimationPlayer>>
+    AnimationPlayerSet;
+
+  AnimationPlayerSet mPlayPendingSet;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_dom_PendingPlayerTracker_h
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -9,18 +9,23 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chr
 
 EXPORTS.mozilla.dom += [
     'Animation.h',
     'AnimationEffect.h',
     'AnimationPlayer.h',
     'AnimationTimeline.h',
 ]
 
+EXPORTS.mozilla += [
+    'PendingPlayerTracker.h',
+]
+
 UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffect.cpp',
     'AnimationPlayer.cpp',
     'AnimationTimeline.cpp',
+    'PendingPlayerTracker.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
--- a/dom/animation/test/css-animations/test_animation-effect-name.html
+++ b/dom/animation/test/css-animations/test_animation-effect-name.html
@@ -1,45 +1,37 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes xyz {
   to { left: 100px }
 }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'xyz 100s';
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Animation effect name matches keyframes rule name');
-  div.remove();
 }, 'Effect name makes keyframe rule');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'x\\yz 100s';
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Escaped animation effect name matches keyframes rule name');
-  div.remove();
 }, 'Escaped animation name');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'x\\79 z 100s';
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
                 'Hex-escaped animation effect name matches keyframes rule'
                 + ' name');
-  div.remove();
 }, 'Animation name with hex-escape');
 
 </script>
--- a/dom/animation/test/css-animations/test_animation-pausing.html
+++ b/dom/animation/test/css-animations/test_animation-pausing.html
@@ -1,182 +1,153 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes anim { 
   0% { margin-left: 0px }
   100% { margin-left: 10000px }
 }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-function waitForFrame() {
-  return new Promise(function(resolve, reject) {
-    window.requestAnimationFrame(resolve);
-  });
-}
-
 function getMarginLeft(cs) {
   return parseFloat(cs.marginLeft);
 }
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s';
 
   var player = div.getAnimationPlayers()[0];
 
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
   var previousAnimVal = getMarginLeft(cs);
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_true(getMarginLeft(cs) > previousAnimVal,
-                  'margin-left is initially increasing');
-      previousAnimVal = getMarginLeft(cs);
+  player.ready.then(waitForFrame).then(t.step_func(function() {
+    assert_true(getMarginLeft(cs) > previousAnimVal,
+                'margin-left is initially increasing');
+    previousAnimVal = getMarginLeft(cs);
 
-      player.pause();
-    });
-    return waitForFrame();
-  }).then(function() {
-    t.step(function() {
-      assert_equals(getMarginLeft(cs), previousAnimVal,
-                    'margin-left does not increase after calling pause()');
-    });
-    div.remove();
+    player.pause();
+    return player.ready.then(waitForFrame);
+  })).then(t.step_func(function() {
+    assert_equals(getMarginLeft(cs), previousAnimVal,
+                  'margin-left does not increase after calling pause()');
     t.done();
-  });
+  }));
 }, 'pause() a running animation');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   player.pause();
   div.style.animationPlayState = 'running';
-  cs.animationPlayState; // Trigger style resolution
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_equals(cs.animationPlayState, 'running',
-                    'animation-play-state is running');
-      assert_equals(getMarginLeft(cs), 0,
-                    'Paused value of margin-left is zero');
-    });
-    div.remove();
+  player.ready.then(waitForFrame).then(t.step_func(function() {
+    assert_equals(cs.animationPlayState, 'running',
+                  'animation-play-state is running');
+    assert_equals(getMarginLeft(cs), 0,
+                  'Paused value of margin-left is zero');
     t.done();
-  });
+  }));
 }, 'pause() overrides animation-play-state');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
 
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   player.play();
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_true(getMarginLeft(cs) > 0,
-                  'Playing value of margin-left is greater than zero');
-    });
-    div.remove();
+  player.ready.then(waitForFrame).then(t.step_func(function() {
+    assert_true(getMarginLeft(cs) > 0,
+                'Playing value of margin-left is greater than zero');
     t.done();
-  });
+  }));
 }, 'play() overrides animation-play-state');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   player.play();
-  div.style.animationPlayState = 'running';
-  cs.animationPlayState; // Trigger style resolution
 
   var previousAnimVal;
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_equals(cs.animationPlayState, 'running',
-                    'animation-play-state is running');
-      previousAnimVal = getMarginLeft(cs);
-      div.style.animationPlayState = 'paused';
-      cs.animationPlayState; // Trigger style resolution
-    });
+  player.ready.then(function() {
+    div.style.animationPlayState = 'running';
+    cs.animationPlayState; // Trigger style resolution
     return waitForFrame();
-  }).then(function() {
-    t.step(function() {
-      assert_equals(cs.animationPlayState, 'paused',
-                    'animation-play-state is paused');
-      assert_equals(getMarginLeft(cs), previousAnimVal,
-                    'Animated value of margin-left does not change when'
-                    + ' paused by style');
-    });
-    div.remove();
+  }).then(t.step_func(function() {
+    assert_equals(cs.animationPlayState, 'running',
+                  'animation-play-state is running');
+    previousAnimVal = getMarginLeft(cs);
+    div.style.animationPlayState = 'paused';
+    cs.animationPlayState; // Trigger style resolution
+    return waitForFrame();
+  })).then(t.step_func(function() {
+    assert_equals(cs.animationPlayState, 'paused',
+                  'animation-play-state is paused');
+    assert_equals(getMarginLeft(cs), previousAnimVal,
+                  'Animated value of margin-left does not change when'
+                  + ' paused by style');
     t.done();
-  });
+  }));
 }, 'play() is overridden by later setting "animation-play-state: paused"');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   // Set the specified style first. If implementations fail to
   // apply the style changes first, they will ignore the redundant
   // call to play() and fail to correctly override the pause style.
   div.style.animationPlayState = 'paused';
   player.play();
   var previousAnimVal = getMarginLeft(cs);
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_equals(cs.animationPlayState, 'paused',
-                    'animation-play-state is paused');
-      assert_true(getMarginLeft(cs) > previousAnimVal,
-                  'Playing value of margin-left is increasing');
-    });
-    div.remove();
+  player.ready.then(waitForFrame).then(t.step_func(function() {
+    assert_equals(cs.animationPlayState, 'paused',
+                  'animation-play-state is paused');
+    assert_true(getMarginLeft(cs) > previousAnimVal,
+                'Playing value of margin-left is increasing');
     t.done();
-  });
+  }));
 }, 'play() flushes pending changes to animation-play-state first');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   // Unlike the previous test for play(), since calling pause() is sticky,
@@ -188,22 +159,19 @@ async_test(function(t) {
   // (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
   //
   // For now this just serves as a sanity check that we do the same thing
   // even if we set style before calling the API.
   div.style.animationPlayState = 'running';
   player.pause();
   var previousAnimVal = getMarginLeft(cs);
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_equals(cs.animationPlayState, 'running',
-                    'animation-play-state is paused');
-      assert_equals(getMarginLeft(cs), previousAnimVal,
-                    'Paused value of margin-left does not change');
-    });
-    div.remove();
+  waitForFrame().then(t.step_func(function() {
+    assert_equals(cs.animationPlayState, 'running',
+                  'animation-play-state is paused');
+    assert_equals(getMarginLeft(cs), previousAnimVal,
+                  'Paused value of margin-left does not change');
     t.done();
-  });
+  }));
 }, 'pause() applies pending changes to animation-play-state first');
 // (Note that we can't actually test for this; see comment above, in test-body.)
 
 </script>
--- a/dom/animation/test/css-animations/test_animation-player-playstate.html
+++ b/dom/animation/test/css-animations/test_animation-player-playstate.html
@@ -1,57 +1,59 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes anim { }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-test(function() {
-  var div = addDiv();
+async_test(function(t) {
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(player.playState, 'running');
+  // Bug 927349: Check for pending state here
+
+  player.ready.then(t.step_func(function() {
+    assert_equals(player.playState, 'running');
+    t.done();
+  }));
 }, 'Player returns correct playState when running');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(player.playState, 'paused');
 }, 'Player returns correct playState when paused');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s';
 
   var player = div.getAnimationPlayers()[0];
   player.pause();
   assert_equals(player.playState, 'paused');
 }, 'Player.playState updates when paused by script');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
 
   var player = div.getAnimationPlayers()[0];
   div.style.animationPlayState = 'running';
   // This test also checks that calling playState flushes style
+  // Bug 927349: Make this check for 'pending'
   assert_equals(player.playState, 'running');
 }, 'Player.playState updates when resumed by setting style');
 
 </script>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/test_animation-player-ready.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
+<div id="log"></div>
+<style>
+@keyframes abc {
+  to { transform: translate(10px) }
+}
+</style>
+<script>
+'use strict';
+
+async_test(function(t) {
+  var div = addDiv(t);
+  div.style.animation = 'abc 100s';
+  var player = div.getAnimationPlayers()[0];
+
+  var originalReadyPromise = player.ready;
+  player.ready.then(function() {
+    assert_equals(player.ready, originalReadyPromise,
+                  'Ready promise is the same object when playing completes');
+    player.pause();
+    // TODO: When we implement deferred pausing (bug 1109390), change this to
+    // assert_not_equals and wait on the new promise before continuing.
+    assert_equals(player.ready, originalReadyPromise,
+                  'Ready promise does not change when pausing (for now)');
+    player.play();
+    assert_not_equals(player.ready, originalReadyPromise,
+                      'Ready promise object identity differs after calling'
+                      + ' play()');
+    t.done();
+  });
+}, 'A new ready promise is created each time play() is called'
+   + ' the animation property');
+
+test(function(t) {
+  var div = addDiv(t);
+  div.style.animation = 'abc 100s paused';
+  var player = div.getAnimationPlayers()[0];
+
+  var originalReadyPromise = player.ready;
+  div.style.animationPlayState = 'running';
+  assert_not_equals(player.ready, originalReadyPromise,
+                    'After updating animation-play-state a new ready promise'
+                    + ' object is created');
+}, 'A new ready promise is created when setting animation-play-state: running');
+
+async_test(function(t) {
+  var div = addDiv(t);
+  div.style.animation = 'abc 100s';
+  var player = div.getAnimationPlayers()[0];
+
+  player.ready.then(function() {
+    var promiseBeforeCallingPlay = player.ready;
+    player.play();
+    assert_equals(player.ready, promiseBeforeCallingPlay,
+                  'Ready promise has same object identity after redundant call'
+                  + ' to play()');
+    t.done();
+  });
+}, 'Redundant calls to play() do not generate new ready promise objects');
+
+async_test(function(t) {
+  var div = addDiv(t);
+  div.style.animation = 'abc 100s';
+  var player = div.getAnimationPlayers()[0];
+
+  player.ready.then(function(resolvedPlayer) {
+    assert_equals(resolvedPlayer, player,
+                  'Object identity of player passed to Promise callback'
+                  + ' matches the player object owning the Promise');
+    t.done();
+  });
+}, 'The ready promise is fulfilled with its AnimationPlayer');
+
+</script>
--- a/dom/animation/test/css-animations/test_animation-target.html
+++ b/dom/animation/test/css-animations/test_animation-target.html
@@ -1,27 +1,21 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes anim { }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim 100s';
   var players = div.getAnimationPlayers();
   assert_equals(players[0].source.target, div,
     'Animation.target is the animatable div');
-  div.remove();
 }, 'Returned CSS animations have the correct Animation.target');
 
 </script>
--- a/dom/animation/test/css-animations/test_animations-dynamic-changes.html
+++ b/dom/animation/test/css-animations/test_animations-dynamic-changes.html
@@ -1,58 +1,59 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes anim1 {
   to { left: 100px }
 }
 @keyframes anim2 { }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s';
 
   var originalPlayer = div.getAnimationPlayers()[0];
-  var originalStartTime = originalPlayer.startTime;
-  var originalCurrentTime = originalPlayer.currentTime;
+  var originalStartTime;
+  var originalCurrentTime;
 
   // Wait a moment so we can confirm the startTime doesn't change (and doesn't
   // simply reflect the current time).
-  window.requestAnimationFrame(t.step_func(function() {
+  originalPlayer.ready.then(function() {
+    originalStartTime = originalPlayer.startTime;
+    originalCurrentTime = originalPlayer.currentTime;
+
+    // Wait a moment so we can confirm the startTime doesn't change (and
+    // doesn't simply reflect the current time).
+    return waitForFrame();
+  }).then(t.step_func(function() {
     div.style.animationDuration = '200s';
     var player = div.getAnimationPlayers()[0];
     assert_equals(player, originalPlayer,
                   'The same AnimationPlayer is returned after updating'
                   + ' animation duration');
     assert_equals(player.startTime, originalStartTime,
                   'AnimationPlayers returned by getAnimationPlayers preserve'
                   + ' their startTime even when they are updated');
     // Sanity check
     assert_not_equals(player.currentTime, originalCurrentTime,
                       'AnimationPlayer.currentTime has updated in next'
                       + ' requestAnimationFrame callback');
-    div.remove();
     t.done();
   }));
 }, 'AnimationPlayers preserve their startTime when changed');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
 
   // Store original state
   var players = div.getAnimationPlayers();
   var player1 = players[0];
   var player2 = players[1];
 
   // Update first in list
@@ -60,91 +61,99 @@ test(function() {
   players = div.getAnimationPlayers();
   assert_equals(players[0], player1,
                 'First player is in same position after update');
   assert_equals(players[1], player2,
                 'Second player is in same position after update');
 }, 'Updated AnimationPlayers maintain their order in the list');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   div.style.animation = 'anim1 200s, anim1 100s';
 
   // Store original state
   var players = div.getAnimationPlayers();
   var player1 = players[0];
   var player2 = players[1];
 
-  // Wait before continuing so we can compare start times
-  window.requestAnimationFrame(t.step_func(function() {
+  // Wait before continuing so we can compare start times (otherwise the
+  // new player objects and existing player objects will all have the same
+  // start time).
+  waitForAllPlayers(players).then(waitForFrame).then(t.step_func(function() {
     // Swap duration of first and second in list and prepend animation at the
     // same time
     div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
     players = div.getAnimationPlayers();
     assert_true(players[0] !== player1 && players[0] !== player2,
                 'New player is prepended to start of list');
     assert_equals(players[1], player1,
                   'First player is in second position after update');
     assert_equals(players[2], player2,
                   'Second player is in third position after update');
     assert_equals(players[1].startTime, players[2].startTime,
                   'Old players have the same start time');
+    // TODO: Check that players[0].startTime === null
+    return players[0].ready;
+  })).then(t.step_func(function() {
     assert_true(players[0].startTime > players[1].startTime,
                 'New player has later start time');
-    div.remove();
     t.done();
   }));
 }, 'Only the startTimes of existing animations are preserved');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
   var secondPlayer = div.getAnimationPlayers()[1];
 
   // Wait before continuing so we can compare start times
-  window.requestAnimationFrame(t.step_func(function() {
+  secondPlayer.ready.then(waitForFrame).then(t.step_func(function() {
     // Trim list of animations
     div.style.animationName = 'anim1';
     var players = div.getAnimationPlayers();
     assert_equals(players.length, 1, 'List of players was trimmed');
     assert_equals(players[0], secondPlayer,
                   'Remaining player is the second one in the list');
+    assert_equals(typeof(players[0].startTime), 'number',
+                  'Remaining player has resolved startTime');
     assert_true(players[0].startTime < players[0].timeline.currentTime,
                 'Remaining player preserves startTime');
-    div.remove();
     t.done();
   }));
 }, 'Animations are removed from the start of the list while preserving'
    + ' the state of existing players');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s';
-  var firstAddedPlayer = div.getAnimationPlayers()[0];
+  var firstAddedPlayer = div.getAnimationPlayers()[0],
+      secondAddedPlayer,
+      players;
 
   // Wait and add second player
-  window.requestAnimationFrame(t.step_func(function() {
+  firstAddedPlayer.ready.then(waitForFrame).then(t.step_func(function() {
     div.style.animation = 'anim1 100s, anim1 100s';
-    var secondAddedPlayer = div.getAnimationPlayers()[0];
+    secondAddedPlayer = div.getAnimationPlayers()[0];
 
     // Wait again and add another player
-    window.requestAnimationFrame(t.step_func(function() {
-      div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
-      var players = div.getAnimationPlayers();
-      assert_not_equals(firstAddedPlayer, secondAddedPlayer,
-                        'New players are added to start of the list');
-      assert_equals(players[0], secondAddedPlayer,
-                    'Second player remains in same position after'
-                    + ' interleaving');
-      assert_equals(players[2], firstAddedPlayer,
-                    'First player remains in same position after'
-                    + ' interleaving');
-      assert_true(players[1].startTime > players[0].startTime,
-                  'Interleaved player starts later than existing players');
-      assert_true(players[0].startTime > players[2].startTime,
-                  'Original players retain their start time');
-      div.remove();
-      t.done();
-    }));
+    return secondAddedPlayer.ready.then(waitForFrame);
+  })).then(t.step_func(function() {
+    div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
+    players = div.getAnimationPlayers();
+    assert_not_equals(firstAddedPlayer, secondAddedPlayer,
+                      'New players are added to start of the list');
+    assert_equals(players[0], secondAddedPlayer,
+                  'Second player remains in same position after'
+                  + ' interleaving');
+    assert_equals(players[2], firstAddedPlayer,
+                  'First player remains in same position after'
+                  + ' interleaving');
+    return players[1].ready;
+  })).then(t.step_func(function() {
+    assert_true(players[1].startTime > players[0].startTime,
+                'Interleaved player starts later than existing players');
+    assert_true(players[0].startTime > players[2].startTime,
+                'Original players retain their start time');
+    t.done();
   }));
 }, 'Player state is preserved when interleaving animations in list');
 
 </script>
--- a/dom/animation/test/css-animations/test_element-get-animation-players.html
+++ b/dom/animation/test/css-animations/test_element-get-animation-players.html
@@ -1,242 +1,238 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <style>
 @keyframes anim1 {
   to { left: 100px }
 }
 @keyframes anim2 {
   to { top: 100px }
 }
 @keyframes multiPropAnim {
   to { background: green, opacity: 0.5, left: 100px, top: 100px }
 }
 @keyframes empty { }
 </style>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   assert_equals(div.getAnimationPlayers().length, 0,
     'getAnimationPlayers returns an empty sequence for an element'
     + ' with no animations');
-  div.remove();
 }, 'getAnimationPlayers for non-animated content');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Add an animation
   div.style.animation = 'anim1 100s';
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 1,
     'getAnimationPlayers returns a player running CSS Animations');
-  var startTime = players[0].startTime;
-  assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
-    'CSS animation has a sensible start time');
+  players[0].ready.then(t.step_func(function() {
+    var startTime = players[0].startTime;
+    assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
+      'CSS animation has a sensible start time');
 
-  // Wait a moment then add a second animation.
-  //
-  // We wait for the next frame so that we can test that the start times of
-  // the animations differ.
-  window.requestAnimationFrame(t.step_func(function() {
+    // Wait a moment then add a second animation.
+    //
+    // We wait for the next frame so that we can test that the start times of
+    // the animations differ.
+    return waitForFrame();
+  })).then(t.step_func(function() {
     div.style.animation = 'anim1 100s, anim2 100s';
     players = div.getAnimationPlayers();
     assert_equals(players.length, 2,
       'getAnimationPlayers returns one player for each value of'
       + ' animation-name');
+    // Wait until both players are ready
+    // (We don't make any assumptions about the order of the players since
+    //  that is the purpose of the following test.)
+    return waitForAllPlayers(players);
+  })).then(t.step_func(function() {
     assert_true(players[0].startTime < players[1].startTime,
       'Additional players for CSS animations start after the original'
       + ' animation and appear later in the list');
-    div.remove();
     t.done();
   }));
 }, 'getAnimationPlayers for CSS Animations');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
 
   // Add an animation that targets multiple properties
   div.style.animation = 'multiPropAnim 100s';
   assert_equals(div.getAnimationPlayers().length, 1,
     'getAnimationPlayers returns only one player for a CSS Animation'
     + ' that targets multiple properties');
-  div.remove();
 }, 'getAnimationPlayers for multi-property animations');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Add an animation
   div.style.backgroundColor = 'red';
   div.style.animation = 'anim1 100s';
   window.getComputedStyle(div).backgroundColor;
 
-  // Wait a moment then add a transition
-  window.requestAnimationFrame(t.step_func(function() {
+  // Wait until a frame after the animation starts, then add a transition
+  var players = div.getAnimationPlayers();
+  players[0].ready.then(waitForFrame).then(t.step_func(function() {
     div.style.transition = 'all 100s';
     div.style.backgroundColor = 'green';
 
-    var players = div.getAnimationPlayers();
+    players = div.getAnimationPlayers();
     assert_equals(players.length, 2,
-      'getAnimationPlayers returns players for both animations and '
+      'getAnimationPlayers returns players for both animations and'
       + ' transitions that run simultaneously');
+    return waitForAllPlayers(players);
+  })).then(t.step_func(function() {
     assert_true(players[0].startTime > players[1].startTime,
-      'players for transitions appear before animations even if they '
+      'players for transitions appear before animations even if they'
       + ' start later');
-    div.remove();
     t.done();
   }));
 }, 'getAnimationPlayers for both CSS Animations and Transitions at once');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Set up event listener
   div.addEventListener('animationend', t.step_func(function() {
     assert_equals(div.getAnimationPlayers().length, 0,
       'getAnimationPlayers does not return players for finished '
       + ' (and non-forwards-filling) CSS Animations');
-    div.remove();
     t.done();
   }));
 
   // Add a very short animation
   div.style.animation = 'anim1 0.01s';
 }, 'getAnimationPlayers for CSS Animations that have finished');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Set up event listener
   div.addEventListener('animationend', t.step_func(function() {
     assert_equals(div.getAnimationPlayers().length, 1,
       'getAnimationPlayers returns players for CSS Animations that have'
       + ' finished but are filling forwards');
-    div.remove();
     t.done();
   }));
 
   // Add a very short animation
   div.style.animation = 'anim1 0.01s forwards';
 }, 'getAnimationPlayers for CSS Animations that have finished but are'
    + ' forwards filling');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'none 100s';
 
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 0,
     'getAnimationPlayers returns an empty sequence for an element'
     + ' with animation-name: none');
 
   div.style.animation = 'none 100s, anim1 100s';
   players = div.getAnimationPlayers();
   assert_equals(players.length, 1,
     'getAnimationPlayers returns players only for those CSS Animations whose'
     + ' animation-name is not none');
-
-  div.remove();
 }, 'getAnimationPlayers for CSS Animations with animation-name: none');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'missing 100s';
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 0,
     'getAnimationPlayers returns an empty sequence for an element'
     + ' with animation-name: missing');
 
   div.style.animation = 'anim1 100s, missing 100s';
   players = div.getAnimationPlayers();
   assert_equals(players.length, 1,
     'getAnimationPlayers returns players only for those CSS Animations whose'
     + ' animation-name is found');
-
-  div.remove();
 }, 'getAnimationPlayers for CSS Animations with animation-name: missing');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s, notyet 100s';
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 1,
     'getAnimationPlayers initally only returns players for CSS Animations whose'
     + ' animation-name is found');
 
-  window.requestAnimationFrame(t.step_func(function() {
+  players[0].ready.then(waitForFrame).then(t.step_func(function() {
     var keyframes = '@keyframes notyet { to { left: 100px; } }';
     document.styleSheets[0].insertRule(keyframes, 0);
     players = div.getAnimationPlayers();
     assert_equals(players.length, 2,
       'getAnimationPlayers includes player when @keyframes rule is added'
       + ' later');
+    return waitForAllPlayers(players);
+  })).then(t.step_func(function() {
     assert_true(players[0].startTime < players[1].startTime,
       'Newly added player has a later start time');
     document.styleSheets[0].deleteRule(0);
-    div.remove();
     t.done();
   }));
 }, 'getAnimationPlayers for CSS Animations where the @keyframes rule is added'
    + ' later');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
   assert_equals(div.getAnimationPlayers().length, 2,
     'getAnimationPlayers returns one player for each CSS animation-name'
     + ' even if the names are duplicated');
-  div.remove();
 }, 'getAnimationPlayers for CSS Animations with duplicated animation-name');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'empty 100s';
   assert_equals(div.getAnimationPlayers().length, 1,
     'getAnimationPlayers returns players for CSS animations with an'
     + ' empty keyframes rule');
-  div.remove();
 }, 'getAnimationPlayers for CSS Animations with empty keyframes rule');
 
-test(function() {
-  var div = addDiv();
+async_test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s 100s';
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 1,
     'getAnimationPlayers returns animations for CSS animations whose'
     + ' delay makes them start later');
-  assert_true(players[0].startTime <= document.timeline.currentTime,
-    'For CSS Animations in delay phase, the start time of the player is'
-    + ' not in the future');
-  div.remove();
+  players[0].ready.then(waitForFrame).then(t.step_func(function() {
+    assert_true(players[0].startTime <= document.timeline.currentTime,
+      'For CSS Animations in delay phase, the start time of the player is'
+      + ' not in the future');
+    t.done();
+  }));
 }, 'getAnimationPlayers for CSS animations in delay phase');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim1 0s 100s';
   assert_equals(div.getAnimationPlayers().length, 1,
     'getAnimationPlayers returns animations for CSS animations whose'
     + ' duration is zero');
   div.remove();
 }, 'getAnimationPlayers for zero-duration CSS Animations');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
   div.style.animation = 'anim1 100s';
   var originalPlayer = div.getAnimationPlayers()[0];
 
   // Update pause state (an AnimationPlayer change)
   div.style.animationPlayState = 'paused';
   var pausedPlayer = div.getAnimationPlayers()[0];
   assert_equals(pausedPlayer.playState, 'paused',
                 'player\'s paused state is updated');
@@ -247,13 +243,11 @@ test(function() {
   // Update duration (an Animation change)
   div.style.animationDuration = '200s';
   var extendedPlayer = div.getAnimationPlayers()[0];
   // FIXME: Check extendedPlayer.source.timing.duration has changed once the
   // API is available
   assert_equals(originalPlayer, extendedPlayer,
                 'getAnimationPlayers returns the same objects even when their'
                 + ' duration changes');
-
-  div.remove();
 }, 'getAnimationPlayers returns objects with the same identity');
 
 </script>
--- a/dom/animation/test/css-transitions/test_animation-effect-name.html
+++ b/dom/animation/test/css-transitions/test_animation-effect-name.html
@@ -1,24 +1,23 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
+test(function(t) {
+  var div = addDiv(t);
 
   // Add a transition
   div.style.left = '0px';
   window.getComputedStyle(div).transitionProperty;
   div.style.transition = 'all 100s';
   div.style.left = '100px';
 
   assert_equals(div.getAnimationPlayers()[0].source.effect.name, '',
                 'Animation effects for transitions have an empty name');
-  div.remove();
 }, 'Effect name for transitions');
 
 </script>
--- a/dom/animation/test/css-transitions/test_animation-pausing.html
+++ b/dom/animation/test/css-transitions/test_animation-pausing.html
@@ -1,66 +1,48 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
-function waitForFrame() {
-  return new Promise(function(resolve, reject) {
-    window.requestAnimationFrame(resolve);
-  });
-}
-
 function getMarginLeft(cs) {
   return parseFloat(cs.marginLeft);
 }
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
   var cs = window.getComputedStyle(div);
 
   div.style.marginLeft = '0px';
   cs.marginLeft; // Flush style to set up transition start point
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '10000px';
   cs.marginLeft;
 
   var player = div.getAnimationPlayers()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
   var previousAnimVal = getMarginLeft(cs);
 
-  waitForFrame().then(function() {
-    t.step(function() {
-      assert_true(getMarginLeft(cs) > previousAnimVal,
-                  'margin-left is initially increasing');
-      previousAnimVal = getMarginLeft(cs);
-      player.pause();
-    });
+  player.ready.then(waitForFrame).then(t.step_func(function() {
+    assert_true(getMarginLeft(cs) > previousAnimVal,
+                'margin-left is initially increasing');
+    previousAnimVal = getMarginLeft(cs);
+    player.pause();
     return waitForFrame();
-  }).then(function() {
-    t.step(function() {
-      assert_equals(getMarginLeft(cs), previousAnimVal,
-                    'margin-left does not increase after calling pause()');
-      previousAnimVal = getMarginLeft(cs);
-      player.play();
-    });
-    return waitForFrame();
-  }).then(function() {
-    t.step(function() {
-      assert_true(getMarginLeft(cs) > previousAnimVal,
-                  'margin-left increases after calling play()');
-    });
-    div.remove();
+  })).then(t.step_func(function() {
+    assert_equals(getMarginLeft(cs), previousAnimVal,
+                  'margin-left does not increase after calling pause()');
+    previousAnimVal = getMarginLeft(cs);
+    player.play();
+    return player.ready.then(waitForFrame);
+  })).then(t.step_func(function() {
+    assert_true(getMarginLeft(cs) > previousAnimVal,
+                'margin-left increases after calling play()');
     t.done();
-  });
+  }));
 }, 'pause() and play() a transition');
 
 </script>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-player-ready.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+
+async_test(function(t) {
+  var div = addDiv(t);
+  div.style.transform = 'translate(0px)';
+  window.getComputedStyle(div).transform;
+  div.style.transition = 'transform 100s';
+  div.style.transform = 'translate(10px)';
+  window.getComputedStyle(div).transform;
+
+  var player = div.getAnimationPlayers()[0];
+  var originalReadyPromise = player.ready;
+
+  player.ready.then(t.step_func(function() {
+    assert_equals(player.ready, originalReadyPromise,
+                  'Ready promise is the same object when playing completes');
+    player.pause();
+    // TODO: When we implement deferred pausing, change this to
+    // assert_not_equals and wait on the new promise before continuing.
+    assert_equals(player.ready, originalReadyPromise,
+                  'Ready promise does not change when pausing (for now)');
+    player.play();
+    assert_not_equals(player.ready, originalReadyPromise,
+                      'Ready promise object identity differs after calling'
+                      + ' play()');
+    t.done();
+  }));
+}, 'A new ready promise is created each time play() is called'
+   + ' the animation property');
+
+</script>
--- a/dom/animation/test/css-transitions/test_animation-target.html
+++ b/dom/animation/test/css-transitions/test_animation-target.html
@@ -1,24 +1,23 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
+test(function(t) {
+  var div = addDiv(t);
 
   div.style.left = '0px';
   window.getComputedStyle(div).transitionProperty;
   div.style.transition = 'left 100s';
   div.style.left = '100px';
 
   var players = div.getAnimationPlayers();
   assert_equals(players[0].source.target, div,
     'Animation.target is the animatable div');
-  div.remove();
 }, 'Returned CSS transitions have the correct Animation.target');
 
 </script>
--- a/dom/animation/test/css-transitions/test_element-get-animation-players.html
+++ b/dom/animation/test/css-transitions/test_element-get-animation-players.html
@@ -1,100 +1,94 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="../testcommon.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-function addDiv() {
-  var div = document.createElement('div');
-  document.body.appendChild(div);
-  return div;
-}
-
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Add a couple of transitions
   div.style.left = '0px';
   div.style.top = '0px';
   window.getComputedStyle(div).transitionProperty;
 
   div.style.transition = 'all 100s';
   div.style.left = '100px';
   div.style.top = '100px';
 
   var players = div.getAnimationPlayers();
   assert_equals(players.length, 2,
     'getAnimationPlayers() returns one player per transitioning property');
-  var startTime = players[0].startTime;
-  assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
-    'CSS transitions have sensible start times');
-  assert_equals(players[0].startTime, players[1].startTime,
-    'CSS transitions started together have the same start time');
-
-  // Wait a moment then add a third transition
-  window.requestAnimationFrame(t.step_func(function() {
+  waitForAllPlayers(players).then(t.step_func(function() {
+    var startTime = players[0].startTime;
+    assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
+                'CSS transitions have sensible start times');
+    assert_equals(players[0].startTime, players[1].startTime,
+      'CSS transitions started together have the same start time');
+    // Wait a moment then add a third transition
+    return waitForFrame();
+  })).then(t.step_func(function() {
     div.style.backgroundColor = 'green';
     players = div.getAnimationPlayers();
     assert_equals(players.length, 3,
       'getAnimationPlayers returns players for all running CSS Transitions');
+    return waitForAllPlayers(players);
+  })).then(t.step_func(function() {
     assert_true(players[1].startTime < players[2].startTime,
       'Player for additional CSS transition starts after the original'
       + ' transitions and appears later in the list');
-    div.remove();
     t.done();
   }));
 }, 'getAnimationPlayers for CSS Transitions');
 
 async_test(function(t) {
-  var div = addDiv();
+  var div = addDiv(t);
 
   // Set up event listener
   div.addEventListener('transitionend', t.step_func(function() {
     assert_equals(div.getAnimationPlayers().length, 0,
       'getAnimationPlayers does not return finished CSS Transitions');
-    div.remove();
     t.done();
   }));
 
   // Add a very short transition
   div.style.left = '0px';
   window.getComputedStyle(div).left;
 
   div.style.transition = 'all 0.01s';
   div.style.left = '100px';
   window.getComputedStyle(div).left;
 }, 'getAnimationPlayers for CSS Transitions that have finished');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
 
   // Try to transition non-animatable property animation-duration
   div.style.animationDuration = '10s';
   window.getComputedStyle(div).animationDuration;
 
   div.style.transition = 'all 100s';
   div.style.animationDuration = '100s';
 
   assert_equals(div.getAnimationPlayers().length, 0,
     'getAnimationPlayers returns an empty sequence for a transition'
     + ' of a non-animatable property');
-  div.remove();
 }, 'getAnimationPlayers for transition on non-animatable property');
 
-test(function() {
-  var div = addDiv();
+test(function(t) {
+  var div = addDiv(t);
 
   div.style.setProperty('-vendor-unsupported', '0px', '');
   window.getComputedStyle(div).transitionProperty;
   div.style.transition = 'all 100s';
   div.style.setProperty('-vendor-unsupported', '100px', '');
 
   assert_equals(div.getAnimationPlayers().length, 0,
     'getAnimationPlayers returns an empty sequence for a transition'
     + ' of an unsupported property');
-  div.remove();
 }, 'getAnimationPlayers for transition on unsupported property');
 
 </script>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,14 +1,20 @@
+[DEFAULT]
+support-files =
+  testcommon.js
+
 [animation-timeline/test_animation-timeline.html]
 skip-if = buildapp == 'mulet'
 [css-animations/test_animations-dynamic-changes.html]
 [css-animations/test_animation-effect-name.html]
 [css-animations/test_animation-pausing.html]
 [css-animations/test_animation-player-playstate.html]
+[css-animations/test_animation-player-ready.html]
 [css-animations/test_animation-target.html]
 [css-animations/test_element-get-animation-players.html]
 skip-if = buildapp == 'mulet'
 [css-transitions/test_animation-effect-name.html]
 [css-transitions/test_animation-pausing.html]
+[css-transitions/test_animation-player-ready.html]
 [css-transitions/test_animation-target.html]
 [css-transitions/test_element-get-animation-players.html]
 skip-if = buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/testcommon.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Appends a div to the document body.
+ *
+ * @param t  The testharness.js Test object. If provided, this will be used
+ *           to register a cleanup callback to remove the div when the test
+ *           finishes.
+ */
+function addDiv(t) {
+  var div = document.createElement('div');
+  document.body.appendChild(div);
+  if (t && typeof t.add_cleanup === 'function') {
+    t.add_cleanup(function() { div.remove(); });
+  }
+  return div;
+}
+
+/**
+ * Promise wrapper for requestAnimationFrame.
+ */
+function waitForFrame() {
+  return new Promise(function(resolve, reject) {
+    window.requestAnimationFrame(resolve);
+  });
+}
+
+/**
+ * Wrapper that takes a sequence of N players and returns:
+ *
+ *   Promise.all([players[0].ready, players[1].ready, ... players[N-1].ready]);
+ */
+function waitForAllPlayers(players) {
+  return Promise.all(players.map(function(player) { return player.ready; }));
+}
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -500,17 +500,26 @@ this.DOMApplicationRegistry = {
         });
       }
     });
   },
 
   // Installs a 3rd party app.
   installPreinstalledApp: function installPreinstalledApp(aId) {
 #ifdef MOZ_WIDGET_GONK
-    let app = this.webapps[aId];
+    // In some cases, the app might be already installed under a different ID but
+    // with the same manifestURL. In that case, the only content of the webapp will
+    // be the id of the old version, which is the one we'll keep.
+    let destId  = this.webapps[aId].oldId || aId;
+    // We don't need the oldId anymore
+    if (destId !== aId) {
+      delete this.webapps[aId];
+    }
+
+    let app = this.webapps[destId];
     let baseDir, isPreinstalled = false;
     try {
       baseDir = FileUtils.getDir("coreAppsDir", ["webapps", aId], false);
       if (!baseDir.exists()) {
         return isPreinstalled;
       } else if (!baseDir.directoryEntries.hasMoreElements()) {
         debug("Error: Core app in " + baseDir.path + " is empty");
         return isPreinstalled;
@@ -540,20 +549,20 @@ this.DOMApplicationRegistry = {
       isPackage = false;
       filesToMove = ["manifest.webapp"];
     } else {
       isPackage = true;
       filesToMove = ["application.zip", "update.webapp"];
     }
 
     debug("Installing 3rd party app : " + aId +
-          " from " + baseDir.path);
-
-    // We copy this app to DIRECTORY_NAME/$aId, and set the base path as needed.
-    let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
+          " from " + baseDir.path + " to " + destId);
+
+    // We copy this app to DIRECTORY_NAME/$destId, and set the base path as needed.
+    let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", destId], true, true);
 
     filesToMove.forEach(function(aFile) {
         let file = baseDir.clone();
         file.append(aFile);
         try {
           file.copyTo(destDir, aFile);
         } catch(e) {
           debug("Error: Failed to copy " + file.path + " to " + destDir.path);
@@ -563,17 +572,17 @@ this.DOMApplicationRegistry = {
     app.installState = "installed";
     app.cachePath = app.basePath;
     app.basePath = OS.Path.dirname(this.appsFile);
 
     if (!isPackage) {
       return isPreinstalled;
     }
 
-    app.origin = "app://" + aId;
+    app.origin = "app://" + destId;
 
     // Do this for all preinstalled apps... we can't know at this
     // point if the updates will be signed or not and it doesn't
     // hurt to have it always.
     app.storeId = STORE_ID_PENDING_PREFIX + app.installOrigin;
 
     // Extract the manifest.webapp file from application.zip.
     let zipFile = baseDir.clone();
@@ -588,17 +597,17 @@ this.DOMApplicationRegistry = {
       }
       let manifestFile = destDir.clone();
       manifestFile.append("manifest.webapp");
       zipReader.extract("manifest.webapp", manifestFile);
     } catch(e) {
       // If we are unable to extract the manifest, cleanup and remove this app.
       debug("Cleaning up: " + e);
       destDir.remove(true);
-      delete this.webapps[aId];
+      delete this.webapps[destId];
     } finally {
       zipReader.close();
     }
     return isPreinstalled;
 #endif
   },
 
   // For hosted apps, uninstall an app served from http:// if we have
@@ -662,17 +671,23 @@ this.DOMApplicationRegistry = {
         delete this.webapps[id];
       }
 
       let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
       // c
       for (let id in data) {
         // Core apps have ids matching their domain name (eg: dialer.gaiamobile.org)
         // Use that property to check if they are new or not.
-        if (!(id in this.webapps)) {
+        // Note that in some cases, the id might change, but the
+        // manifest URL wont. So consider that the app is old if
+        // the id does not exist already and if there's no other id
+        // for the manifestURL.
+        var oldId = (id in this.webapps) ? id :
+                      this._appIdForManifestURL(data[id].manifestURL);
+        if (!oldId) {
           this.webapps[id] = data[id];
           this.webapps[id].basePath = appDir.path;
 
           this.webapps[id].id = id;
 
           // Create a new localId.
           this.webapps[id].localId = this._nextLocalId();
 
@@ -683,21 +698,27 @@ this.DOMApplicationRegistry = {
         } else {
           // Fields that we must not update. Confere bug 993011 comment 10.
           let fieldsBlacklist = ["basePath", "id", "installerAppId",
             "installerIsBrowser", "localId", "receipts", "storeId",
             "storeVersion"];
           // we fall into this case if the app is present in /system/b2g/webapps/webapps.json
           // and in /data/local/webapps/webapps.json: this happens when updating gaia apps
           // Confere bug 989876
+          // We also should fall in this case when the app is a preinstalled third party app.
           for (let field in data[id]) {
             if (fieldsBlacklist.indexOf(field) === -1) {
-              this.webapps[id][field] = data[id][field];
+              this.webapps[oldId][field] = data[id][field];
             }
           }
+          // If the id for the app has changed on the update, keep a pointer to the old one
+          // since we'll need this to update the app files.
+          if (id !== oldId) {
+            this.webapps[id] = {oldId: oldId};
+          }
         }
       }
     }.bind(this)).then(null, Cu.reportError);
   },
 
   loadAndUpdateApps: function() {
     return Task.spawn(function() {
       let runUpdate = AppsUtils.isFirstRun(Services.prefs);
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -564,18 +564,16 @@ AutoIncumbentScript::AutoIncumbentScript
   : ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false)
   , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
 {
 }
 
 AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
   : ScriptSettingsStackEntry()
 {
-  MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(),
-                !JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread()));
   if (aIsMainThread) {
     mCxPusher.emplace(static_cast<JSContext*>(nullptr),
                       /* aAllowNull = */ true);
   }
 }
 
 danger::AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull)
 {
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -370,17 +370,17 @@ nsDOMWindowUtils::SetDisplayPortForEleme
                      nsPresContext::CSSPixelsToAppUnits(aYPx),
                      nsPresContext::CSSPixelsToAppUnits(aWidthPx),
                      nsPresContext::CSSPixelsToAppUnits(aHeightPx));
 
   content->SetProperty(nsGkAtoms::DisplayPort,
                        new DisplayPortPropertyData(displayport, aPriority),
                        nsINode::DeleteProperty<DisplayPortPropertyData>);
 
-  if (nsLayoutUtils::UsesAsyncScrolling()) {
+  if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
     nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
     if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
       // We are setting a root displayport for a document.
       // The pres shell needs a special flag set.
       presShell->SetIgnoreViewportScrolling(true);
     }
   }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1990,16 +1990,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayerTracker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
@@ -2073,16 +2074,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayerTracker)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
 
   tmp->mParentDocument = nullptr;
@@ -5261,17 +5263,17 @@ nsIDocument::InsertAnonymousContent(Elem
   return anonymousContent.forget();
 }
 
 void
 nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
                                     ErrorResult& aRv)
 {
   nsIPresShell* shell = GetShell();
-  if (!shell) {
+  if (!shell || !shell->GetCanvasFrame()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsCOMPtr<Element> container = shell->GetCanvasFrame()
                                      ->GetCustomContentContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -7386,16 +7388,26 @@ nsDocument::GetAnimationController()
   // because they don't get OnPageShow / OnPageHide calls).
   if (!mIsShowing && !mIsBeingUsedAsImage) {
     mAnimationController->OnPageHide();
   }
 
   return mAnimationController;
 }
 
+PendingPlayerTracker*
+nsDocument::GetOrCreatePendingPlayerTracker()
+{
+  if (!mPendingPlayerTracker) {
+    mPendingPlayerTracker = new PendingPlayerTracker();
+  }
+
+  return mPendingPlayerTracker;
+}
+
 /**
  * Retrieve the "direction" property of the document.
  *
  * @lina 01/09/2001
  */
 NS_IMETHODIMP
 nsDocument::GetDir(nsAString& aDirection)
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -54,16 +54,17 @@
 #include "nsILoadContext.h"
 #include "nsIProgressEventSink.h"
 #include "nsISecurityEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "imgIRequest.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/PendingPlayerTracker.h"
 #include "mozilla/dom/DOMImplementation.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMXPathEvaluator.h"
 #include "jsfriendapi.h"
 #include "ImportManager.h"
@@ -1042,16 +1043,25 @@ public:
     EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
 
   nsTArray<nsCString> mHostObjectURIs;
 
   // Returns our (lazily-initialized) animation controller.
   // If HasAnimationController is true, this is guaranteed to return non-null.
   nsSMILAnimationController* GetAnimationController() MOZ_OVERRIDE;
 
+  virtual mozilla::PendingPlayerTracker*
+  GetPendingPlayerTracker() MOZ_FINAL
+  {
+    return mPendingPlayerTracker;
+  }
+
+  virtual mozilla::PendingPlayerTracker*
+  GetOrCreatePendingPlayerTracker() MOZ_OVERRIDE;
+
   void SetImagesNeedAnimating(bool aAnimating) MOZ_OVERRIDE;
 
   virtual void SuppressEventHandling(SuppressionType aWhat,
                                      uint32_t aIncrease) MOZ_OVERRIDE;
 
   virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat,
                                                     bool aFireEvents) MOZ_OVERRIDE;
 
@@ -1506,16 +1516,20 @@ protected:
 
   nsCOMArray<nsIStyleSheet> mStyleSheets;
   nsCOMArray<nsIStyleSheet> mOnDemandBuiltInUASheets;
   nsCOMArray<nsIStyleSheet> mAdditionalSheets[SheetTypeCount];
 
   // Array of observers
   nsTObserverArray<nsIDocumentObserver*> mObservers;
 
+  // Tracker for animation players that are waiting to start.
+  // nullptr until GetOrCreatePendingPlayerTracker is called.
+  nsRefPtr<mozilla::PendingPlayerTracker> mPendingPlayerTracker;
+
   // Weak reference to the scope object (aka the script global object)
   // that, unlike mScriptGlobalObject, is never unset once set. This
   // is a weak reference to avoid leaks due to circular references.
   nsWeakPtr mScopeObject;
 
   // Stack of full-screen elements. When we request full-screen we push the
   // full-screen element onto this stack, and when we cancel full-screen we
   // pop one off this stack, restoring the previous full-screen state
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -81,16 +81,17 @@ class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 struct nsCSSSelectorList;
 
 namespace mozilla {
 class CSSStyleSheet;
 class ErrorResult;
 class EventStates;
+class PendingPlayerTracker;
 class SVGAttrAnimationRuleProcessor;
 
 namespace css {
 class Loader;
 class ImageLoader;
 } // namespace css
 
 namespace dom {
@@ -1817,16 +1818,27 @@ public:
   // will have a non-null return value.
   bool HasAnimationController()  { return !!mAnimationController; }
 
   // Getter for this document's SMIL Animation Controller. Performs lazy
   // initialization, if this document supports animation and if
   // mAnimationController isn't yet initialized.
   virtual nsSMILAnimationController* GetAnimationController() = 0;
 
+  // Gets the tracker for animation players that are waiting to start.
+  // Returns nullptr if there is no pending player tracker for this document
+  // which will be the case if there have never been any CSS animations or
+  // transitions on elements in the document.
+  virtual mozilla::PendingPlayerTracker* GetPendingPlayerTracker() = 0;
+
+  // Gets the tracker for animation players that are waiting to start and
+  // creates it if it doesn't already exist. As a result, the return value
+  // will never be nullptr.
+  virtual mozilla::PendingPlayerTracker* GetOrCreatePendingPlayerTracker() = 0;
+
   // Makes the images on this document capable of having their animation
   // active or suspended. An Image will animate as long as at least one of its
   // owning Documents needs it to animate; otherwise it can suspend.
   virtual void SetImagesNeedAnimating(bool aAnimating) = 0;
 
   enum SuppressionType {
     eAnimationsOnly = 0x1,
 
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -34,8 +34,10 @@ IPDL_SOURCES += [
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
+if not CONFIG['CLANG_CXX']:
+    FAIL_ON_WARNINGS = True
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2986,16 +2986,24 @@ void HTMLMediaElement::LoadAborted()
 }
 
 void HTMLMediaElement::Error(uint16_t aErrorCode)
 {
   NS_ASSERTION(aErrorCode == nsIDOMMediaError::MEDIA_ERR_DECODE ||
                aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
                aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
                "Only use nsIDOMMediaError codes!");
+
+  // Since we have multiple paths calling into DecodeError, e.g.
+  // MediaKeys::Terminated and EMEH264Decoder::Error. We should take the 1st
+  // one only in order not to fire multiple 'error' events.
+  if (mError) {
+    return;
+  }
+
   mError = new MediaError(this, aErrorCode);
   DispatchAsyncEvent(NS_LITERAL_STRING("error"));
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
     DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
   } else {
     ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
   }
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -99,16 +99,26 @@ size_t MediaDecoderReader::SizeOfVideoQu
 
 size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
 {
   AudioQueueMemoryFunctor functor;
   mAudioQueue.LockedForEach(functor);
   return functor.mSize;
 }
 
+size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
+{
+  return mVideoQueue.GetSize();
+}
+
+size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
+{
+  return mAudioQueue.GetSize();
+}
+
 nsresult MediaDecoderReader::ResetDecode()
 {
   nsresult res = NS_OK;
 
   VideoQueue().Reset();
   AudioQueue().Reset();
 
   mAudioDiscontinuity = true;
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -183,16 +183,19 @@ public:
   // Returns the number of bytes of memory allocated by structures/frames in
   // the video queue.
   size_t SizeOfVideoQueueInBytes() const;
 
   // Returns the number of bytes of memory allocated by structures/frames in
   // the audio queue.
   size_t SizeOfAudioQueueInBytes() const;
 
+  virtual size_t SizeOfVideoQueueInFrames();
+  virtual size_t SizeOfAudioQueueInFrames();
+
   // Only used by WebMReader and MediaOmxReader for now, so stub here rather
   // than in every reader than inherits from MediaDecoderReader.
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
   virtual int64_t GetEvictionOffset(double aTime) { return -1; }
 
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -618,16 +618,19 @@ MediaDecoderStateMachine::DecodeVideo()
     currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
 
     // Time the video decode, so that if it's slow, we can increase our low
     // audio threshold to reduce the chance of an audio underrun while we're
     // waiting for a video decode to complete.
     mVideoDecodeStartTime = TimeStamp::Now();
   }
 
+  SAMPLE_LOG("DecodeVideo() queued=%i, decoder-queued=%o, skip=%i, time=%lld",
+             VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame, currentTime);
+
   mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
          ->Then(DecodeTaskQueue(), __func__, this,
                 &MediaDecoderStateMachine::OnVideoDecoded,
                 &MediaDecoderStateMachine::OnVideoNotDecoded);
 }
 
 bool
 MediaDecoderStateMachine::NeedToDecodeAudio()
@@ -664,16 +667,20 @@ MediaDecoderStateMachine::DecodeAudio()
     // We don't want to consider skipping to the next keyframe if we've
     // only just started up the decode loop, so wait until we've decoded
     // some audio data before enabling the keyframe skip logic on audio.
     if (mIsAudioPrerolling &&
         GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) {
       mIsAudioPrerolling = false;
     }
   }
+
+  SAMPLE_LOG("DecodeAudio() queued=%i, decoder-queued=%o",
+             AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
+
   mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
                                     &MediaDecoderStateMachine::OnAudioDecoded,
                                     &MediaDecoderStateMachine::OnAudioNotDecoded);
 }
 
 bool
 MediaDecoderStateMachine::IsAudioSeekComplete()
 {
@@ -2755,17 +2762,19 @@ void MediaDecoderStateMachine::RenderVid
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
 
   if (aData->mDuplicate) {
     return;
   }
 
-  VERBOSE_LOG("playing video frame %lld", aData->mTime);
+  VERBOSE_LOG("playing video frame %lld (queued=%i, state-machine=%i, decoder-queued=%i)",
+              aData->mTime, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
+              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
                                aTarget);
     MOZ_ASSERT(container->GetFrameDelay() >= 0 || mScheduler->IsRealTime());
   }
 }
--- a/dom/media/bridge/moz.build
+++ b/dom/media/bridge/moz.build
@@ -21,8 +21,10 @@ LOCAL_INCLUDES += [
     '/media/webrtc/',
     '/media/webrtc/signaling/src/common/time_profiling',
     '/media/webrtc/signaling/src/media-conduit',
     '/media/webrtc/signaling/src/mediapipeline',
     '/media/webrtc/signaling/src/peerconnection',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/dom/media/eme/CDMCallbackProxy.cpp
+++ b/dom/media/eme/CDMCallbackProxy.cpp
@@ -324,13 +324,13 @@ CDMCallbackProxy::Decrypted(uint32_t aId
 
   mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
 }
 
 void
 CDMCallbackProxy::Terminated()
 {
   MOZ_ASSERT(mProxy->IsOnGMPThread());
-
-  mProxy->gmp_Terminated();
+  nsRefPtr<nsIRunnable> task = NS_NewRunnableMethod(mProxy, &CDMProxy::Terminated);
+  NS_DispatchToMainThread(task);
 }
 
 } // namespace mozilla
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -564,16 +564,18 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
       mDecryptionJobs.RemoveElementAt(i);
       return;
     }
   }
   NS_WARNING("GMPDecryptorChild returned incorrect job ID");
 }
 
 void
-CDMProxy::gmp_Terminated()
+CDMProxy::Terminated()
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
-  gmp_Shutdown();
+  if (!mKeys.IsNull()) {
+    mKeys->Terminated();
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -99,16 +99,19 @@ public:
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   void RemoveSession(const nsAString& aSessionId,
                      PromiseId aPromiseId);
 
   // Main thread only.
   void Shutdown();
 
+  // Main thread only.
+  void Terminated();
+
   // Threadsafe.
   const nsCString& GetNodeId() const;
 
   // Main thread only.
   void OnResolveNewSessionPromise(uint32_t aPromiseId,
                                   const nsAString& aSessionId);
 
   // Main thread only.
@@ -152,19 +155,16 @@ public:
   // Threadsafe.
   const nsString& KeySystem() const;
 
   // GMP thread only.
   void gmp_Decrypted(uint32_t aId,
                      GMPErr aResult,
                      const nsTArray<uint8_t>& aDecryptedData);
 
-  // GMP thread only.
-  void gmp_Terminated();
-
   CDMCaps& Capabilites();
 
   // Main thread only.
   void OnKeysChange(const nsAString& aSessionId);
 
 #ifdef DEBUG
   bool IsOnGMPThread();
 #endif
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -61,16 +61,53 @@ RejectPromises(const uint32_t& aKey,
   return PL_DHASH_NEXT;
 }
 
 MediaKeys::~MediaKeys()
 {
   Shutdown();
 }
 
+static PLDHashOperator
+CopySessions(const nsAString& aKey,
+             nsRefPtr<MediaKeySession>& aSession,
+             void* aClosure)
+{
+  KeySessionHashMap* p = static_cast<KeySessionHashMap*>(aClosure);
+  p->Put(aSession->GetSessionId(), aSession);
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+CloseSessions(const nsAString& aKey,
+              nsRefPtr<MediaKeySession>& aSession,
+              void* aClosure)
+{
+  aSession->OnClosed();
+  return PL_DHASH_NEXT;
+}
+
+void
+MediaKeys::Terminated()
+{
+  KeySessionHashMap keySessions;
+  // Remove entries during iteration will screw it. Make a copy first.
+  mKeySessions.Enumerate(&CopySessions, &keySessions);
+  keySessions.Enumerate(&CloseSessions, nullptr);
+  keySessions.Clear();
+  MOZ_ASSERT(mKeySessions.Count() == 0);
+
+  // Notify the element about that CDM has terminated.
+  if (mElement) {
+    mElement->DecodeError();
+  }
+
+  Shutdown();
+}
+
 void
 MediaKeys::Shutdown()
 {
   if (mProxy) {
     mProxy->Shutdown();
     mProxy = nullptr;
   }
 
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -99,16 +99,20 @@ public:
   void RejectPromise(PromiseId aId, nsresult aExceptionCode);
   // Resolves promise with "undefined".
   void ResolvePromise(PromiseId aId);
 
   const nsCString& GetNodeId() const;
 
   void Shutdown();
 
+  // Called by CDMProxy when CDM crashes or shuts down. It is different from
+  // Shutdown which is called from the script/dom side.
+  void Terminated();
+
   // Returns true if this MediaKeys has been bound to a media element.
   bool IsBoundToMediaElement() const;
 
   // Return NS_OK if the principals are the same as when the MediaKeys
   // was created, failure otherwise.
   nsresult CheckPrincipals();
 
 private:
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -642,16 +642,36 @@ MP4Reader::PopSample(TrackType aTrack)
       }
       return mDemuxer->DemuxVideoSample();
 
     default:
       return nullptr;
   }
 }
 
+size_t
+MP4Reader::SizeOfVideoQueueInFrames()
+{
+  return SizeOfQueue(kVideo);
+}
+
+size_t
+MP4Reader::SizeOfAudioQueueInFrames()
+{
+  return SizeOfQueue(kAudio);
+}
+
+size_t
+MP4Reader::SizeOfQueue(TrackType aTrack)
+{
+  auto& decoder = GetDecoderData(aTrack);
+  MonitorAutoLock lock(decoder.mMonitor);
+  return decoder.mOutput.Length() + (decoder.mNumSamplesInput - decoder.mNumSamplesOutput);
+}
+
 nsresult
 MP4Reader::ResetDecode()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   Flush(kVideo);
   mDemuxer->SeekVideo(0);
   Flush(kAudio);
   mDemuxer->SeekAudio(0);
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -32,16 +32,19 @@ class MP4Reader MOZ_FINAL : public Media
 
 public:
   explicit MP4Reader(AbstractMediaDecoder* aDecoder);
 
   virtual ~MP4Reader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
 
+  virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
+  virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
+
   virtual nsRefPtr<VideoDataPromise>
   RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
 
   virtual bool HasAudio() MOZ_OVERRIDE;
   virtual bool HasVideo() MOZ_OVERRIDE;
 
@@ -112,16 +115,18 @@ private:
   void UpdateIndex();
   bool IsSupportedAudioMimeType(const char* aMimeType);
   bool IsSupportedVideoMimeType(const char* aMimeType);
   void NotifyResourcesStatusChanged();
   void RequestCodecResource();
   bool IsWaitingOnCodecResource();
   virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE;
 
+  size_t SizeOfQueue(TrackType aTrack);
+
   nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
   nsAutoPtr<PlatformDecoderModule> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
   public:
     DecoderCallback(MP4Reader* aReader,
                     mp4_demuxer::TrackType aType)
       : mReader(aReader)
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -88,16 +88,36 @@ MediaSourceReader::IsWaitingMediaResourc
     if (!mEssentialTrackBuffers[i]->IsReady()) {
       return true;
     }
   }
 
   return !mHasEssentialTrackBuffers;
 }
 
+size_t
+MediaSourceReader::SizeOfVideoQueueInFrames()
+{
+  if (!mVideoReader) {
+    MSE_DEBUG("MediaSourceReader(%p)::SizeOfVideoQueue called with no video reader", this);
+    return 0;
+  }
+  return mVideoReader->SizeOfVideoQueueInFrames();
+}
+
+size_t
+MediaSourceReader::SizeOfAudioQueueInFrames()
+{
+  if (!mAudioReader) {
+    MSE_DEBUG("MediaSourceReader(%p)::SizeOfAudioQueue called with no audio reader", this);
+    return 0;
+  }
+  return mAudioReader->SizeOfAudioQueueInFrames();
+}
+
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MediaSourceReader::RequestAudioData()
 {
   nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
   if (!mAudioReader) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
     mAudioPromise.Reject(DECODE_ERROR, __func__);
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -45,16 +45,19 @@ public:
   void PrepareInitialization();
 
   bool IsWaitingMediaResources() MOZ_OVERRIDE;
 
   nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
   nsRefPtr<VideoDataPromise>
   RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
 
+  virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
+  virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
+
   void OnAudioDecoded(AudioData* aSample);
   void OnAudioNotDecoded(NotDecodedReason aReason);
   void OnVideoDecoded(VideoData* aSample);
   void OnVideoNotDecoded(NotDecodedReason aReason);
 
   void OnSeekCompleted();
   void OnSeekFailed(nsresult aResult);
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -9,16 +9,17 @@
 #include "ContainerParser.h"
 #include "MediaSourceDecoder.h"
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 #include "SourceBufferDecoder.h"
 #include "SourceBufferResource.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
+#include "mozilla/Preferences.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
@@ -38,16 +39,17 @@ TrackBuffer::TrackBuffer(MediaSourceDeco
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
   mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
+  mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
 }
 
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
 }
 
 class ReleaseDecoderTask : public nsRunnable {
@@ -145,17 +147,18 @@ TrackBuffer::AppendData(const uint8_t* a
     MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
     return false;
   }
 
   int64_t start, end;
   if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
     if (mParser->IsMediaSegmentPresent(aData, aLength) &&
         mLastEndTimestamp &&
-        !mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value())) {
+        (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
+         mDecoderPerSegment)) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       // This data is earlier in the timeline than data we have already
       // processed, so we must create a new decoder to handle the decoding.
       if (!decoders.NewDecoder()) {
         return false;
       }
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -157,12 +157,13 @@ private:
   Maybe<int64_t> mLastEndTimestamp;
 
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
+  bool mDecoderPerSegment;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */
--- a/dom/media/omx/mediaresourcemanager/moz.build
+++ b/dom/media/omx/mediaresourcemanager/moz.build
@@ -31,8 +31,10 @@ CXXFLAGS += [
         'frameworks/base/include/utils',
         'frameworks/base/include/media/',
         'frameworks/base/include/media/stagefright/openmax',
         'frameworks/base/media/libstagefright/include',
     ]
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -100,8 +100,10 @@ CXXFLAGS += [
         'frameworks/native/opengl/include',
         'frameworks/native/include',
         'hardware/libhardware/include/',
     ]
 ]
 
 if CONFIG['ANDROID_VERSION'] > '15':
     DEFINES['MOZ_OMX_WEBM_DECODER'] = True
+
+FAIL_ON_WARNINGS = True
--- a/dom/plugins/ipc/BrowserStreamChild.cpp
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -166,17 +166,17 @@ BrowserStreamChild::NPN_RequestRead(NPBy
   AssertPluginThread();
 
   if (ALIVE != mState || kStreamOpen != mStreamStatus)
     return NPERR_GENERIC_ERROR;
 
   IPCByteRanges ranges;
   for (; aRangeList; aRangeList = aRangeList->next) {
     IPCByteRange br = {aRangeList->offset, aRangeList->length};
-    ranges.push_back(br);
+    ranges.AppendElement(br);
   }
 
   NPError result;
   CallNPN_RequestRead(ranges, &result);
   return result;
 }
 
 void
--- a/dom/plugins/ipc/BrowserStreamParent.cpp
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -53,26 +53,26 @@ BrowserStreamParent::AnswerNPN_RequestRe
   default:
     NS_ERROR("Unexpected state");
     return false;
   }
 
   if (!mStream)
     return false;
 
-  if (ranges.size() > INT32_MAX)
+  if (ranges.Length() > INT32_MAX)
     return false;
 
-  nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.size()]);
-  for (uint32_t i = 0; i < ranges.size(); ++i) {
+  nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.Length()]);
+  for (uint32_t i = 0; i < ranges.Length(); ++i) {
     rp[i].offset = ranges[i].offset;
     rp[i].length = ranges[i].length;
     rp[i].next = &rp[i + 1];
   }
-  rp[ranges.size() - 1].next = nullptr;
+  rp[ranges.Length() - 1].next = nullptr;
 
   *result = mNPP->mNPNIface->requestread(mStream, rp);
   return true;
 }
 
 bool
 BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
 {
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -70,17 +70,17 @@ extern PRLogModuleInfo* GetPluginLog();
  * This is NPByteRange without the linked list.
  */
 struct IPCByteRange
 {
   int32_t offset;
   uint32_t length;
 };  
 
-typedef std::vector<IPCByteRange> IPCByteRanges;
+typedef nsTArray<IPCByteRange> IPCByteRanges;
 
 typedef nsCString Buffer;
 
 struct NPRemoteWindow
 {
   NPRemoteWindow();
   uint64_t window;
   int32_t x;
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -266,54 +266,54 @@ CSPService::ShouldProcess(uint32_t      
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
                                    nsIChannel *newChannel,
                                    uint32_t flags,
                                    nsIAsyncVerifyRedirectCallback *callback)
 {
   nsAsyncRedirectAutoCallback autoCallback(callback);
 
+  nsCOMPtr<nsIURI> newUri;
+  nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // No need to continue processing if CSP is disabled or if the protocol
+  // is *not* subject to CSP.
+  // Please note, the correct way to opt-out of CSP using a custom
+  // protocolHandler is to set one of the nsIProtocolHandler flags
+  // that are whitelistet in subjectToCSP()
+  if (!sCSPEnabled || !subjectToCSP(newUri)) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsILoadInfo> loadInfo;
-  nsresult rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+  rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
 
   // if no loadInfo on the channel, nothing for us to do
   if (!loadInfo) {
     return NS_OK;
   }
 
-  // The loadInfo must not necessarily contain a Node, hence we try to query
-  // the CSP in the following order:
-  //   a) Get the Node, the Principal of that Node, and the CSP of that Principal
-  //   b) Get the Principal and the CSP of that Principal
-
-  nsCOMPtr<nsINode> loadingNode = loadInfo->LoadingNode();
-  nsCOMPtr<nsIPrincipal> principal = loadingNode ?
-                                     loadingNode->NodePrincipal() :
-                                     loadInfo->LoadingPrincipal();
-  NS_ASSERTION(principal, "Can not evaluate CSP without a principal");
+  // Get the LoadingPrincipal and CSP from the loadInfo
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
+  rv = loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // if there is no CSP, nothing for us to do
   if (!csp) {
     return NS_OK;
   }
 
   /* Since redirecting channels don't call into nsIContentPolicy, we call our
    * Content Policy implementation directly when redirects occur using the
    * information set in the LoadInfo when channels are created.
    *
    * We check if the CSP permits this host for this type of load, if not,
    * we cancel the load now.
    */
-
-  nsCOMPtr<nsIURI> newUri;
-  rv = newChannel->GetURI(getter_AddRefs(newUri));
-  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIURI> originalUri;
   rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
   NS_ENSURE_SUCCESS(rv, rv);
   nsContentPolicyType policyType = loadInfo->GetContentPolicyType();
 
   int16_t aDecision = nsIContentPolicy::ACCEPT;
   csp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
                   newUri,         // nsIURI
@@ -326,22 +326,24 @@ CSPService::AsyncOnChannelRedirect(nsICh
 #ifdef PR_LOGGING
   if (newUri) {
     nsAutoCString newUriSpec("None");
     newUri->GetSpec(newUriSpec);
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect called for %s",
             newUriSpec.get()));
   }
-  if (aDecision == 1)
+  if (aDecision == 1) {
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
-  else
+  }
+  else {
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
+  }
 #endif
 
   // if ShouldLoad doesn't accept the load, cancel the request
   if (!NS_CP_ACCEPTED(aDecision)) {
     autoCallback.DontCallback();
     return NS_BINDING_FAILED;
   }
   return NS_OK;
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -144,20 +144,16 @@ CSP_ContentTypeToDirective(nsContentPoli
       return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_FONT:
       return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_MEDIA:
       return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
 
-    // TYPE_DOCUMENT shouldn't be used since it's specifically whitelisted by
-    // the CSPService, but in case we do want to know which directive to check,
-    // FRAME_SRC is the best fit.
-    case nsIContentPolicy::TYPE_DOCUMENT:
     case nsIContentPolicy::TYPE_SUBDOCUMENT:
       return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_WEBSOCKET:
     case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
     case nsIContentPolicy::TYPE_BEACON:
     case nsIContentPolicy::TYPE_FETCH:
       return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
@@ -167,18 +163,23 @@ CSP_ContentTypeToDirective(nsContentPoli
       return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_XBL:
     case nsIContentPolicy::TYPE_PING:
     case nsIContentPolicy::TYPE_DTD:
     case nsIContentPolicy::TYPE_OTHER:
       return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
 
-    // CSP can not block csp reports, fall through to error
+    // csp shold not block top level loads, e.g. in case
+    // of a redirect.
+    case nsIContentPolicy::TYPE_DOCUMENT:
+    // CSP can not block csp reports
     case nsIContentPolicy::TYPE_CSP_REPORT:
+      return nsIContentSecurityPolicy::NO_DIRECTIVE;
+
     // Fall through to error for all other directives
     default:
       MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
   }
   return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
 }
 
 nsCSPHostSrc*
--- a/dom/settings/SettingsRequestManager.jsm
+++ b/dom/settings/SettingsRequestManager.jsm
@@ -20,16 +20,21 @@ let VERBOSE = false;
 
 try {
   DEBUG   =
     Services.prefs.getBoolPref("dom.mozSettings.SettingsRequestManager.debug.enabled");
   VERBOSE =
     Services.prefs.getBoolPref("dom.mozSettings.SettingsRequestManager.verbose.enabled");
 } catch (ex) { }
 
+let allowForceReadOnly = false;
+try {
+  allowForceReadOnly = Services.prefs.getBoolPref("dom.mozSettings.allowForceReadOnly");
+} catch (ex) { }
+
 function debug(s) {
   dump("-*- SettingsRequestManager: " + s + "\n");
 }
 
 const kXpcomShutdownObserverTopic      = "xpcom-shutdown";
 const kInnerWindowDestroyed            = "inner-window-destroyed";
 const kMozSettingsChangedObserverTopic = "mozsettings-changed";
 const kSettingsReadSuffix              = "-read";
@@ -115,16 +120,22 @@ function SettingsLockInfo(aDB, aMsgMgr, 
     _failed: false,
     // If we're slated to run finalize, set this to make sure we don't
     // somehow run other events afterward.
     finalizing: false,
     // Lets us know if we can use this lock for a clear command
     canClear: true,
     // Lets us know if this lock has been used to clear at any point.
     hasCleared: false,
+    // forceReadOnly sets whether we want to do a read only transaction. Define
+    // true by default, and let queueTask() set this to false if we queue any
+    // "set" task. Since users of settings locks will queue all tasks before
+    // any idb transaction is created, we know we will have all needed
+    // information to set this before creating a transaction.
+    forceReadOnly: true,
     // Principal the lock was created under. We assume that the lock
     // will continue to exist under this principal for the duration of
     // its lifetime.
     principal: aPrincipal,
     getObjectStore: function() {
       if (VERBOSE) debug("Getting transaction for " + this.lockID);
       let store;
       // Test for transaction validity via trying to get the
@@ -141,17 +152,18 @@ function SettingsLockInfo(aDB, aMsgMgr, 
             throw e;
           }
         }
       }
       // Create one transaction with a global permission. This may be
       // slightly slower on apps with full settings permissions, but
       // it means we don't have to do our own transaction order
       // bookkeeping.
-      if (!SettingsPermissions.hasSomeWritePermission(this.principal)) {
+      let canReadOnly = allowForceReadOnly && this.forceReadOnly;
+      if (canReadOnly || !SettingsPermissions.hasSomeWritePermission(this.principal)) {
         if (VERBOSE) debug("Making READONLY transaction for " + this.lockID);
         this._transaction = aDB._db.transaction(SETTINGSSTORE_NAME, "readonly");
       } else {
         if (VERBOSE) debug("Making READWRITE transaction for " + this.lockID);
         this._transaction = aDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
       }
       this._transaction.oncomplete = function() {
         if (VERBOSE) debug("Transaction for lock " + this.lockID + " closed");
@@ -249,17 +261,21 @@ let SettingsRequestManager = {
     if (!lock) {
       return Promise.reject({error: "Lock already dead, cannot queue task"});
     }
 
     if (aOperation == "set") {
       aData.settings = this._serializePreservingBinaries(aData.settings);
     }
 
-    this.lockInfo[aData.lockID].tasks.push({
+    if (aOperation === "set" || aOperation === "clear") {
+      lock.forceReadOnly = false;
+    }
+
+    lock.tasks.push({
       operation: aOperation,
       data: aData,
       defer: defer
     });
 
     let promise = new Promise(function(resolve, reject) {
       defer.resolve = resolve;
       defer.reject = reject;
--- a/dom/webidl/AnimationPlayer.webidl
+++ b/dom/webidl/AnimationPlayer.webidl
@@ -23,19 +23,20 @@ interface AnimationPlayer {
   readonly attribute double? startTime;
   [BinaryName="currentTimeAsDouble"]
   readonly attribute double? currentTime;
 
   /* Not yet implemented
            attribute double             playbackRate; */
   [BinaryName="playStateFromJS"]
   readonly attribute AnimationPlayState playState;
+  [Throws]
+  readonly attribute Promise<AnimationPlayer> ready;
   /*
-  readonly attribute Promise            ready;
-  readonly attribute Promise            finished;
+  readonly attribute Promise<AnimationPlayer> finished;
   void cancel ();
   void finish ();
   */
   [BinaryName="playFromJS"]
   void play ();
   [BinaryName="pauseFromJS"]
   void pause ();
   /*
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -325,16 +325,19 @@ private:
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
   DECL_GFX_PREF(Once, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
   DECL_GFX_PREF(Once, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
 
   DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
 
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.add-test-devices",               VRAddTestDevices, int32_t, 1);
+
+  // This and code dependent on it should be removed once containerless scrolling looks stable.
+  DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
   {
     MOZ_ASSERT(!sInstanceHasBeenDestroyed, "Should never recreate a gfxPrefs instance!");
     if (!sInstance) {
       sInstance = new gfxPrefs;
     }
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -259,17 +259,16 @@ CreateSimdClass(JSContext *cx,
         return nullptr;
 
     typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
-    typeDescr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
 
     if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
         return nullptr;
 
     // Create prototype property, which inherits from Object.prototype.
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1430,17 +1430,17 @@ TypedObject::GetBuffer(JSContext *cx, un
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject &obj = args[0].toObject();
     ArrayBufferObject *buffer;
     if (obj.is<OutlineTransparentTypedObject>())
         buffer = obj.as<OutlineTransparentTypedObject>().getOrCreateBuffer(cx);
     else
         buffer = obj.as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
     if (!buffer)
-        MOZ_CRASH();
+        return false;
     args.rval().setObject(*buffer);
     return true;
 }
 
 /* static */ bool
 TypedObject::GetByteOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1590,16 +1590,18 @@ OutlineTypedObject::createDerived(JSCont
 }
 
 /*static*/ TypedObject *
 TypedObject::createZeroed(JSContext *cx, HandleTypeDescr descr, int32_t length, gc::InitialHeap heap)
 {
     // If possible, create an object with inline data.
     if ((size_t) descr->size() <= InlineTypedObject::MaximumSize) {
         InlineTypedObject *obj = InlineTypedObject::create(cx, descr, heap);
+        if (!obj)
+            return nullptr;
         descr->initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
         return obj;
     }
 
     // Create unattached wrapper object.
     Rooted<OutlineTypedObject*> obj(cx, OutlineTypedObject::createUnattached(cx, descr, length, heap));
     if (!obj)
         return nullptr;
@@ -3177,42 +3179,39 @@ TraceListVisitor::fillList(Vector<int32_
 
 static bool
 CreateTraceList(JSContext *cx, HandleTypeDescr descr)
 {
     // Trace lists are only used for inline typed objects. We don't use them
     // for larger objects, both to limit the size of the trace lists and
     // because tracing outline typed objects is considerably more complicated
     // than inline ones.
-    if ((size_t) descr->size() > InlineTypedObject::MaximumSize || descr->transparent()) {
-        descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
+    if ((size_t) descr->size() > InlineTypedObject::MaximumSize || descr->transparent())
         return true;
-    }
 
     TraceListVisitor visitor;
     visitReferences(*descr, nullptr, visitor);
 
     Vector<int32_t> entries(cx);
     if (!visitor.fillList(entries))
         return false;
 
     // Trace lists aren't necessary for descriptors with no references.
     MOZ_ASSERT(entries.length() >= 3);
-    if (entries.length() == 3) {
-        descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
+    if (entries.length() == 3)
         return true;
-    }
 
     int32_t *list = cx->pod_malloc<int32_t>(entries.length());
     if (!list)
         return false;
 
     PodCopy(list, entries.begin(), entries.length());
 
     descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(list));
     return true;
 }
 
 /* static */ void
 TypeDescr::finalize(FreeOp *fop, JSObject *obj)
 {
-    js_free(const_cast<int32_t *>(obj->as<TypeDescr>().traceList()));
+    if (obj->as<TypeDescr>().hasTraceList())
+        js_free(const_cast<int32_t *>(obj->as<TypeDescr>().traceList()));
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -173,23 +173,27 @@ class TypeDescr : public NativeObject
         return getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32();
     }
 
     // Type descriptors may contain a list of their references for use during
     // scanning. Marking code is optimized to use this list to mark inline
     // typed objects, rather than the slower trace hook. This list is only
     // specified when (a) the descriptor is short enough that it can fit in an
     // InlineTypedObject, and (b) the descriptor contains at least one
-    // reference. Otherwise it is null.
+    // reference. Otherwise its value is undefined.
     //
     // The list is three consecutive arrays of int32_t offsets, with each array
     // terminated by -1. The arrays store offsets of string, object, and value
     // references in the descriptor, in that order.
+    bool hasTraceList() const {
+        return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
+    }
     const int32_t *traceList() const {
-        return reinterpret_cast<int32_t *>(getReservedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
+        MOZ_ASSERT(hasTraceList());
+        return reinterpret_cast<int32_t *>(getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
     }
 
     void initInstances(const JSRuntime *rt, uint8_t *mem, size_t length);
     void traceInstances(JSTracer *trace, uint8_t *mem, size_t length);
 
     static void finalize(FreeOp *fop, JSObject *obj);
 };
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1781,19 +1781,20 @@ GCMarker::processMarkStackTop(SliceBudge
         } else if (v.isSymbol()) {
             markAndScanSymbol(obj, v.toSymbol());
         }
     }
     return;
 
   scan_typed_obj:
     {
-        const int32_t *list = obj->as<InlineOpaqueTypedObject>().typeDescr().traceList();
-        if (!list)
+        TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
+        if (!descr->hasTraceList())
             return;
+        const int32_t *list = descr->traceList();
         uint8_t *memory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
         while (*list != -1) {
             JSString *str = *reinterpret_cast<JSString **>(memory + *list);
             markAndScanString(obj, str);
             list++;
         }
         list++;
         while (*list != -1) {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1876,34 +1876,34 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
 
     Register ptrReg = ToRegister(ptr);
 
     if (!mir->needsBoundsCheck()) {
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             if (size == 32)
-                masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
+                masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Always);
             else
-                masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
+                masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Always);
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
         memoryBarrier(mir->barrierAfter());
         return;
     }
 
     BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->value()));
         if (size == 32)
-            masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
+            masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Below);
         else
-            masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below);
+            masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Below);
     } else {
         masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->value()), Offset, Assembler::Below);
     }
     memoryBarrier(mir->barrierAfter());
     masm.append(AsmJSHeapAccess(bo.getOffset()));
 }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1755,20 +1755,21 @@ MacroAssemblerARM::ma_vstr(VFPRegister s
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, const Operand &addr, Condition cc)
 {
     return ma_vdtr(IsStore, addr, src, cc);
 }
 BufferOffset
-MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc)
+MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
+                           int32_t offset, Condition cc)
 {
     as_add(ScratchRegister, base, lsl(index, shift), NoSetCond, cc);
-    return ma_vstr(src, Operand(ScratchRegister, 0), cc);
+    return ma_vstr(src, Operand(ScratchRegister, offset), cc);
 }
 
 void
 MacroAssemblerARMCompat::buildFakeExitFrame(Register scratch, uint32_t *offset)
 {
     DebugOnly<uint32_t> initialDepth = framePushed();
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
 
@@ -3626,48 +3627,62 @@ MacroAssemblerARMCompat::storePayload(Re
     MOZ_CRASH("why do we do all of these things?");
 
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value &val, const BaseIndex &dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
-    MOZ_ASSERT(dest.offset == 0);
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
         ma_mov(ImmGCPtr((gc::Cell *)jv.s.payload.ptr), ScratchRegister);
     else
         ma_mov(Imm32(jv.s.payload.i32), ScratchRegister);
 
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
+    // If an offset is used, modify the base so that a [base + index << shift]
+    // instruction format can be used.
+    if (dest.offset != 0)
+        ma_add(dest.base, Imm32(dest.offset), dest.base);
+
     as_dtr(IsStore, 32, Offset, ScratchRegister,
            DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
+
+    // Restore the original value of the base, if necessary.
+    if (dest.offset != 0)
+        ma_sub(dest.base, Imm32(dest.offset), dest.base);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex &dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
     MOZ_ASSERT(shift < 32);
-    MOZ_ASSERT(dest.offset == 0);
 
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
+    // Save/restore the base if the BaseIndex has an offset, as above.
+    if (dest.offset != 0)
+        ma_add(dest.base, Imm32(dest.offset), dest.base);
+
     // Technically, shift > -32 can be handle by changing LSL to ASR, but should
     // never come up, and this is one less code path to get wrong.
     as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
+
+    if (dest.offset != 0)
+        ma_sub(dest.base, Imm32(dest.offset), dest.base);
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, Operand dest) {
     if (dest.getTag() == Operand::MEM) {
         ma_mov(tag, secondScratchReg_);
         ma_str(secondScratchReg_, ToType(dest));
         return;
@@ -3678,30 +3693,29 @@ MacroAssemblerARMCompat::storeTypeTag(Im
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex &dest)
 {
     Register base = dest.base;
     Register index = dest.index;
     unsigned shift = ScaleToShift(dest.scale);
-    MOZ_ASSERT(dest.offset == 0);
     MOZ_ASSERT(base != ScratchRegister);
     MOZ_ASSERT(index != ScratchRegister);
 
     // A value needs to be store a value int base + index << shift + 4.
     // ARM cannot handle this in a single operand, so a temp register is
     // required. However, the scratch register is presently in use to hold the
     // immediate that is being stored into said memory location. Work around
     // this by modifying the base so the valid [base + index << shift] format
     // can be used, then restore it.
-    ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
+    ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
     ma_mov(tag, ScratchRegister);
     ma_str(ScratchRegister, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
-    ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
+    ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
 }
 
 // ARM says that all reads of pc will return 8 higher than the address of the
 // currently executing instruction. This means we are correctly storing the
 // address of the instruction after the call in the register.
 //
 // Also ION is breaking the ARM EABI here (sort of). The ARM EABI says that a
 // function call should move the pc into the link register, then branch to the
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -393,17 +393,18 @@ class MacroAssemblerARM : public Assembl
 
     BufferOffset ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc = Always);
     BufferOffset ma_vldr(const Operand &addr, VFPRegister dest, Condition cc = Always);
     BufferOffset ma_vldr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always);
 
     BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always);
     BufferOffset ma_vstr(VFPRegister src, const Operand &addr, Condition cc = Always);
 
-    BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always);
+    BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
+                         int32_t offset, Condition cc = Always);
     // Calls an Ion function, assumes that the stack is untouched (8 byte
     // aligned).
     void ma_callJit(const Register reg);
     // Calls an Ion function, assuming that sp has already been decremented.
     void ma_callJitNoPush(const Register reg);
     // Calls an ion function, assuming that the stack is currently not 8 byte
     // aligned.
     void ma_callJitHalfPush(const Register reg);
@@ -1412,33 +1413,29 @@ class MacroAssemblerARMCompat : public M
     template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address &address);
     void storePtr(Register src, const BaseIndex &address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_vstr(src, Operand(addr));
     }
     void storeDouble(FloatRegister src, BaseIndex addr) {
-        // Harder cases not handled yet.
-        MOZ_ASSERT(addr.offset == 0);
         uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-        ma_vstr(src, addr.base, addr.index, scale);
+        ma_vstr(src, addr.base, addr.index, scale, addr.offset);
     }
     void moveDouble(FloatRegister src, FloatRegister dest) {
         ma_vmov(src, dest);
     }
 
     void storeFloat32(FloatRegister src, Address addr) {
         ma_vstr(VFPRegister(src).singleOverlay(), Operand(addr));
     }
     void storeFloat32(FloatRegister src, BaseIndex addr) {
-        // Harder cases not handled yet.
-        MOZ_ASSERT(addr.offset == 0);
         uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-        ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale);
+        ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale, addr.offset);
     }
 
   private:
     template<typename T>
     Register computePointer(const T &src, Register r);
 
     template<typename T>
     void compareExchangeARMv6(int nbytes, bool signExtend, const T &mem, Register oldval,
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5260,13 +5260,18 @@ TypeScript::printTypes(JSContext *cx, Ha
 
 void
 TypeObject::setAddendum(AddendumKind kind, void *addendum)
 {
     MOZ_ASSERT(!needsSweep());
     MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
     MOZ_ASSERT(!(flags_ & OBJECT_FLAG_ADDENDUM_MASK));
 
-    writeBarrierPre(this);
+    // Manually trigger barriers if we are clearing a TypeNewScript. Other
+    // kinds of addendums are immutable.
+    if (addendum_) {
+        MOZ_ASSERT(kind == Addendum_NewScript);
+        TypeNewScript::writeBarrierPre(newScript());
+    }
 
     flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
     addendum_ = addendum;
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -980,16 +980,18 @@ class TypeNewScript
 
   public:
     TypeNewScript() { mozilla::PodZero(this); }
     ~TypeNewScript() {
         js_free(preliminaryObjects);
         js_free(initializerList);
     }
 
+    static inline void writeBarrierPre(TypeNewScript *newScript);
+
     bool analyzed() const {
         if (preliminaryObjects) {
             MOZ_ASSERT(!templateObject());
             MOZ_ASSERT(!initializerList);
             MOZ_ASSERT(!initializedShape());
             MOZ_ASSERT(!initializedType());
             return false;
         }
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1295,16 +1295,27 @@ TypeObject::getProperty(unsigned i)
     MOZ_ASSERT(i < getPropertyCount());
     if (basePropertyCount() == 1) {
         MOZ_ASSERT(i == 0);
         return (Property *) propertySet;
     }
     return propertySet[i];
 }
 
+inline void
+TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
+{
+    if (!newScript->fun->runtimeFromAnyThread()->needsIncrementalBarrier())
+        return;
+
+    JS::Zone *zone = newScript->fun->zoneFromAnyThread();
+    if (zone->needsIncrementalBarrier())
+        newScript->trace(zone->barrierTracer());
+}
+
 } } /* namespace js::types */
 
 inline js::types::TypeScript *
 JSScript::types()
 {
     maybeSweepTypes(nullptr);
     return types_;
 }
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3029,19 +3029,21 @@ ContainerState::ProcessDisplayItems(nsDi
       }
       if (itemType == nsDisplayItem::TYPE_SCROLL_LAYER ||
           itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) {
         nsDisplayScrollLayer* scrollItem = static_cast<nsDisplayScrollLayer*>(item);
         newLayerEntry->mOpaqueForAnimatedGeometryRootParent =
             scrollItem->IsDisplayPortOpaque();
         newLayerEntry->mBaseFrameMetrics =
             scrollItem->ComputeFrameMetrics(ownLayer, mParameters);
-      } else if (itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
-                 itemType == nsDisplayItem::TYPE_ZOOM ||
-                 itemType == nsDisplayItem::TYPE_RESOLUTION) {
+      } else if ((itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
+                  itemType == nsDisplayItem::TYPE_ZOOM ||
+                  itemType == nsDisplayItem::TYPE_RESOLUTION) &&
+                 gfxPrefs::LayoutUseContainersForRootFrames())
+      {
         newLayerEntry->mBaseFrameMetrics =
           static_cast<nsDisplaySubDocument*>(item)->ComputeFrameMetrics(ownLayer, mParameters);
       }
 
       /**
        * No need to allocate geometry for items that aren't
        * part of a PaintedLayer.
        */
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1498,33 +1498,35 @@ already_AddRefed<LayerManager> nsDisplay
   if (!root) {
     layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
     return nullptr;
   }
   // Root is being scaled up by the X/Y resolution. Scale it back down.
   root->SetPostScale(1.0f/containerParameters.mXScale,
                      1.0f/containerParameters.mYScale);
 
-  bool isRoot = presContext->IsRootContentDocument();
-
-  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
-
-  nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
-
-  root->SetFrameMetrics(
-    nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame,
-                       aBuilder->FindReferenceFrameFor(frame),
-                       root, FrameMetrics::NULL_SCROLL_ID, viewport,
-                       !isRoot, isRoot, containerParameters));
+  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+    bool isRoot = presContext->IsRootContentDocument();
+
+    nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+
+    nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
+
+    root->SetFrameMetrics(
+      nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame,
+                         aBuilder->FindReferenceFrameFor(frame),
+                         root, FrameMetrics::NULL_SCROLL_ID, viewport,
+                         !isRoot, isRoot, containerParameters));
+  }
 
   // NS_WARNING is debug-only, so don't even bother checking the conditions in
   // a release build.
 #ifdef DEBUG
   bool usingDisplayport = false;
-  if (rootScrollFrame) {
+  if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
     nsIContent* content = rootScrollFrame->GetContent();
     if (content) {
       usingDisplayport = nsLayoutUtils::GetDisplayPort(content, nullptr);
     }
   }
   if (usingDisplayport &&
       !(root->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
       SpammyLayoutWarningsEnabled()) {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1009,17 +1009,17 @@ nsLayoutUtils::SetDisplayPortMargins(nsI
     return false;
   }
 
   aContent->SetProperty(nsGkAtoms::DisplayPortMargins,
                         new DisplayPortMarginsPropertyData(
                             aMargins, aPriority),
                         nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
 
-  if (nsLayoutUtils::UsesAsyncScrolling()) {
+  if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
     nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
     if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) {
       // We are setting a root displayport for a document.
       // If we have APZ, then set a special flag on the pres shell so
       // that we don't get scrollbars drawn.
       aPresShell->SetIgnoreViewportScrolling(true);
     }
   }
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
-skip-if = toolkit == 'android' #SLOW_DIRECTORY
+# Android: SLOW_DIRECTORY; Mulet: bug 1048441, bug 1087611, bug 1112988, etc.
+skip-if = toolkit == 'android' || buildapp == 'mulet'
 support-files =
   border_radius_hit_testing_iframe.html
   preserve3d_sorting_hit_testing_iframe.html
   image_rgrg-256x256.png
   image_rrgg-256x256.png
   bug369950-subframe.xml
   file_bug842853.sjs
   file_bug842853.html
@@ -181,30 +182,28 @@ support-files =
 [test_bug582181-2.html]
 [test_bug588174.html]
 [test_bug607529.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
 support-files = file_bug607529.html
 [test_bug667512.html]
 [test_bug677878.html]
 [test_bug696020.html]
-skip-if = buildapp == 'mulet' #Bug 1048441, unexplained almost permanent orange on mulet
 [test_event_target_radius.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040987
 [test_event_target_iframe_oop.html]
 skip-if = e10s # bug 1020135, nested oop iframes not supported
 support-files = bug921928_event_target_iframe_apps_oop.html
 [test_mozPaintCount.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(depends on plugins support) b2g-debug(depends on plugins support) b2g-desktop(depends on plugins support)
 [test_scroll_event_ordering.html]
 [test_bug583889.html]
 support-files = bug583889_inner1.html bug583889_inner2.html
 [test_bug582771.html]
 [test_bug968148.html]
-skip-if = buildapp == 'mulet' # bug 1087611
 support-files = bug968148_inner.html
 [test_bug603550.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT # b2g(Components.classes[@mozilla.org/widget/dragservice;1] is undefined) b2g-debug(Components.classes[@mozilla.org/widget/dragservice;1] is undefined) b2g-desktop(Components.classes[@mozilla.org/widget/dragservice;1] is undefined)
 [test_bug629838.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(depends on plugins support) b2g-debug(depends on plugins support) b2g-desktop(depends on plugins support)
 [test_bug646757.html]
 [test_bug718809.html]
 [test_bug725426.html]
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2842,16 +2842,18 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   // scrollbars are visible, the layer's visible rectangle would be the size
   // of the viewport, so most layer implementations would create a layer buffer
   // that's much larger than necessary. Creating independent layers for each
   // scrollbar works around the problem.
   bool createLayersForScrollbars = mIsRoot &&
     mOuter->PresContext()->IsRootContentDocument();
 
   if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
+    // Root scrollframes have FrameMetrics and clipping on their container
+    // layers, so don't apply clipping again.
     mAddClipRectToLayer = false;
 
     // If we are a root scroll frame that has a display port we want to add
     // scrollbars, they will be children of the scrollable layer, but they get
     // adjusted by the APZC automatically.
     bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
         nsLayoutUtils::GetDisplayPort(mOuter->GetContent());
     bool addScrollBars = mIsRoot && usingDisplayPort && !aBuilder->IsForEventDelivery();
@@ -2872,16 +2874,18 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       // Add overlay scrollbars.
       AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, usingDisplayPort,
                           createLayersForScrollbars, true);
     }
 
     return;
   }
 
+  // Root scrollframes have FrameMetrics and clipping on their container
+  // layers, so don't apply clipping again.
   mAddClipRectToLayer =
     !(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden());
 
   // Overflow clipping can never clip frames outside our subtree, so there
   // is no need to worry about whether we are a moving frame that might clip
   // non-moving frames.
   // Not all our descendants will be clipped by overflow clipping, but all
   // the ones that aren't clipped will be out of flow frames that have already
@@ -2890,28 +2894,40 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   // dirty rect here.
   nsRect dirtyRect = aDirtyRect.Intersect(mScrollPort);
 
   nsRect displayPort;
   bool usingDisplayport = false;
   if (aBuilder->IsPaintingToWindow()) {
     bool wasUsingDisplayPort = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), nullptr);
 
-    if (!mIsRoot) {
+    if (mIsRoot) {
+      if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+        // For a root frame in a container, just get the value of the existing
+        // display port if any.
+        usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
+      } else {
+        // Override the value of the display port base rect, and possibly create a
+        // display port if there isn't one already.
+        nsRect displayportBase = dirtyRect;
+        if (mIsRoot && mOuter->PresContext()->IsRootContentDocument()) {
+          displayportBase =
+            nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
+        }
+        usingDisplayport = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
+              *aBuilder, mOuter, displayportBase, &displayPort);
+      }
+    } else {
       // For a non-root scroll frame, override the value of the display port
       // base rect, and possibly create a display port if there isn't one
       // already. For root scroll frame, nsLayoutUtils::PaintFrame or
       // nsSubDocumentFrame::BuildDisplayList takes care of this.
       nsRect displayportBase = dirtyRect;
       usingDisplayport = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
           *aBuilder, mOuter, displayportBase, &displayPort);
-    } else {
-      // For a root frame, just get the value of the existing display port, if
-      // any.
-      usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
     }
 
     bool usingLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
     if (usingDisplayport && usingLowPrecision) {
       // If we have low-res painting enabled we should check the critical displayport too
       nsRect critDp;
       nsLayoutUtils::GetCriticalDisplayPort(mOuter->GetContent(), &critDp);
     }
@@ -2963,20 +2979,22 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   mShouldBuildScrollableLayer = usingDisplayport || nsContentUtils::HasScrollgrab(mOuter->GetContent());
   bool shouldBuildLayer = false;
   if (mShouldBuildScrollableLayer) {
     shouldBuildLayer = true;
   } else {
     shouldBuildLayer =
       nsLayoutUtils::WantSubAPZC() &&
       WantAsyncScroll() &&
-      // If we are the root scroll frame for the display root then we don't need a scroll
-      // info layer to make a ComputeFrameMetrics call for us as
-      // nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
-      (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
+      // If we are using containers for root frames, and we are the root
+      // scroll frame for the display root, then we don't need a scroll
+      // info layer. nsDisplayList::PaintForFrame already calls
+      // ComputeFrameMetrics for us.
+      (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
+       (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
   }
 
   if (aBuilder->IsPaintingToWindow() &&
       !mShouldBuildScrollableLayer &&
       shouldBuildLayer)
   {
     if (nsDisplayLayerEventRegions *eventRegions = aBuilder->GetLayerEventRegions()) {
       // Make sure that APZ will dispatch events back to content so we can
@@ -3114,32 +3132,32 @@ void
 ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer,
                                        nsIFrame* aContainerReferenceFrame,
                                        const ContainerLayerParameters& aParameters,
                                        nsRect* aClipRect,
                                        nsTArray<FrameMetrics>* aOutput) const
 {
   nsRect scrollport = mScrollPort +
     mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
-  // Root scrollframes have FrameMetrics and clipping on their container layers,
-  // so don't also apply clipping here.
   if (mAddClipRectToLayer) {
     *aClipRect = scrollport;
   }
 
   if (!mShouldBuildScrollableLayer || BuildScrollContainerLayers()) {
     return;
   }
 
   MOZ_ASSERT(mScrolledFrame->GetContent());
 
+  bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
+
   *aOutput->AppendElement() =
       nsDisplayScrollLayer::ComputeFrameMetrics(mScrolledFrame, mOuter,
         aContainerReferenceFrame, aLayer, mScrollParentID,
-        scrollport, false, false, aParameters);
+        scrollport, false, isRoot, aParameters);
 }
 
 bool
 ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
 {
   // Use the right rect depending on if a display port is set.
   nsRect displayPort;
   bool usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -445,16 +445,17 @@ public:
   // by placing descendant content into its own layer(s)
   bool mHasBeenScrolledRecently:1;
   // If true, the resizer is collapsed and not displayed
   bool mCollapsedResizer:1;
 
   // If true, the layer should always be active because we always build a
   // scrollable layer. Used for asynchronous scrolling.
   bool mShouldBuildScrollableLayer:1;
+
   // If true, add clipping in ScrollFrameHelper::ComputeFrameMetrics.
   bool mAddClipRectToLayer:1;
 
   // True if this frame has been scrolled at least once
   bool mHasBeenScrolled:1;
 
   // True if the frame's resolution has been set via SetResolution or restored
   // via RestoreState.
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -408,26 +408,28 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   nsIFrame* savedIgnoreScrollFrame = nullptr;
   if (subdocRootFrame) {
     // get the dirty rect relative to the root frame of the subdoc
     dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
     // and convert into the appunits of the subdoc
     dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
 
     if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
-      // for root content documents we want the base to be the composition bounds
-      nsRect displayportBase = presContext->IsRootContentDocument() ?
-          nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
-          dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
-      nsRect displayPort;
-      if (aBuilder->IsPaintingToWindow() &&
-          nsLayoutUtils::GetOrMaybeCreateDisplayPort(
-            *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
-        haveDisplayPort = true;
-        dirty = displayPort;
+      if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+        // for root content documents we want the base to be the composition bounds
+        nsRect displayportBase = presContext->IsRootContentDocument() ?
+            nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
+            dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
+        nsRect displayPort;
+        if (aBuilder->IsPaintingToWindow() &&
+            nsLayoutUtils::GetOrMaybeCreateDisplayPort(
+              *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
+          haveDisplayPort = true;
+          dirty = displayPort;
+        }
       }
 
       ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
       if (ignoreViewportScrolling) {
         savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
         aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
 
         if (aBuilder->IsForImageVisibility()) {
@@ -448,19 +450,25 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   if (ShouldClipSubdocument()) {
     clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
   }
 
   nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
   bool constructResolutionItem = subdocRootFrame &&
     (presShell->GetXResolution() != 1.0 || presShell->GetYResolution() != 1.0);
   bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
-  bool needsOwnLayer = constructResolutionItem || constructZoomItem ||
-    haveDisplayPort ||
-    presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive(aBuilder));
+  bool needsOwnLayer = false;
+  if (constructResolutionItem ||
+      constructZoomItem ||
+      haveDisplayPort ||
+      presContext->IsRootContentDocument() ||
+      (sf && sf->IsScrollingActive(aBuilder)))
+  {
+    needsOwnLayer = true;
+  }
 
   nsDisplayList childItems;
 
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     if (needsOwnLayer) {
       // Clear current clip. There's no point in propagating it down, since
       // the layer we will construct will be clipped by the current clip.
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -229,16 +229,19 @@ struct AnimationPlayerCollection : publi
                       "must call destructor through element property dtor");
     MOZ_COUNT_DTOR(AnimationPlayerCollection);
     PR_REMOVE_LINK(this);
     mManager->ElementCollectionRemoved();
   }
 
   void Destroy()
   {
+    for (size_t playerIdx = mPlayers.Length(); playerIdx-- != 0; ) {
+      mPlayers[playerIdx]->Cancel();
+    }
     // This will call our destructor.
     mElement->DeleteProperty(mElementProperty);
   }
 
   static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
                            void *aPropertyValue, void *aData);
 
   void Tick();
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -21,16 +21,23 @@
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using mozilla::dom::Animation;
 using mozilla::dom::AnimationPlayer;
 using mozilla::CSSAnimationPlayer;
 
+mozilla::dom::Promise*
+CSSAnimationPlayer::GetReady(ErrorResult& aRv)
+{
+  FlushStyle();
+  return AnimationPlayer::GetReady(aRv);
+}
+
 void
 CSSAnimationPlayer::Play()
 {
   mPauseShouldStick = false;
   AnimationPlayer::Play();
 }
 
 void
@@ -319,29 +326,35 @@ nsAnimationManager::CheckAnimationRule(n
           }
 
           // Replace new animation with the (updated) old one and remove the
           // old one from the array so we don't try to match it any more.
           //
           // Although we're doing this while iterating this is safe because
           // we're not changing the length of newPlayers and we've finished
           // iterating over the list of old iterations.
+          newPlayer->Cancel();
           newPlayer = nullptr;
           newPlayers.ReplaceElementAt(newIdx, oldPlayer);
           collection->mPlayers.RemoveElementAt(oldIdx);
         }
       }
     } else {
       collection =
         GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), true);
     }
     collection->mPlayers.SwapElements(newPlayers);
     collection->mNeedsRefreshes = true;
     collection->Tick();
 
+    // Cancel removed animations
+    for (size_t newPlayerIdx = newPlayers.Length(); newPlayerIdx-- != 0; ) {
+      newPlayers[newPlayerIdx]->Cancel();
+    }
+
     TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
     UpdateStyleAndEvents(collection, refreshTime,
                          EnsureStyleRule_IsNotThrottled);
     // We don't actually dispatch the mPendingEvents now.  We'll either
     // dispatch them the next time we get a refresh driver notification
     // or the next time somebody calls
     // nsPresShell::FlushPendingNotifications.
     if (!mPendingEvents.IsEmpty()) {
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -15,16 +15,19 @@
 
 class nsCSSKeyframesRule;
 class nsStyleContext;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 } /* namespace css */
+namespace dom {
+class Promise;
+} /* namespace dom */
 
 struct AnimationEventInfo {
   nsRefPtr<mozilla::dom::Element> mElement;
   mozilla::InternalAnimationEvent mEvent;
 
   AnimationEventInfo(mozilla::dom::Element *aElement,
                      const nsSubstring& aAnimationName,
                      uint32_t aMessage,
@@ -58,16 +61,17 @@ public:
     , mPauseShouldStick(false)
     , mLastNotification(LAST_NOTIFICATION_NONE)
   {
   }
 
   virtual CSSAnimationPlayer*
   AsCSSAnimationPlayer() MOZ_OVERRIDE { return this; }
 
+  virtual dom::Promise* GetReady(ErrorResult& aRv) MOZ_OVERRIDE;
   virtual void Play() MOZ_OVERRIDE;
   virtual void Pause() MOZ_OVERRIDE;
 
   virtual dom::AnimationPlayState PlayStateFromJS() const MOZ_OVERRIDE;
   virtual void PlayFromJS() MOZ_OVERRIDE;
 
   void PlayFromStyle();
   void PauseFromStyle();
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -295,16 +295,17 @@ nsTransitionManager::StyleContextChanged
       if ((checkProperties &&
            !allTransitionProperties.HasProperty(prop.mProperty)) ||
           // properties whose computed values changed but delay and
           // duration are both zero
           !ExtractComputedValueForTransition(prop.mProperty, aNewStyleContext,
                                              currentValue) ||
           currentValue != segment.mToValue) {
         // stop the transition
+        player->Cancel();
         players.RemoveElementAt(i);
         collection->UpdateAnimationGeneration(mPresContext);
       }
     } while (i != 0);
 
     if (players.IsEmpty()) {
       collection->Destroy();
       collection = nullptr;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4411,8 +4411,25 @@ pref("dom.beforeAfterKeyboardEvent.enabl
 
 // Use raw ICU instead of CoreServices API in Unicode collation
 #ifdef XP_MACOSX
 pref("intl.collation.mac.use_icu", true);
 #endif
 
 // Enable meta-viewport support in remote APZ-enabled frames.
 pref("dom.meta-viewport.enabled", false);
+
+// MozSettings debugging prefs for each component
+pref("dom.mozSettings.SettingsDB.debug.enabled", false);
+pref("dom.mozSettings.SettingsManager.debug.enabled", false);
+pref("dom.mozSettings.SettingsRequestManager.debug.enabled", false);
+pref("dom.mozSettings.SettingsService.debug.enabled", false);
+
+// MozSettings verbose mode to track everything
+pref("dom.mozSettings.SettingsDB.verbose.enabled", false);
+pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
+pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
+pref("dom.mozSettings.SettingsService.verbose.enabled", false);
+
+// Controlling whether we want to allow forcing some Settings
+// IndexedDB transactions to be opened as readonly or keep everything as
+// readwrite.
+pref("dom.mozSettings.allowForceReadOnly", false);
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -80,8 +80,11 @@ if CONFIG['MOZ_LINKER']:
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     SOURCES += [
         'cpuacct.c',
     ]
 
 DEFINES['IMPL_MFBT'] = True
 
 LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
+
+if not CONFIG['_MSC_VER']:
+    FAIL_ON_WARNINGS = True
--- a/mozglue/linker/moz.build
+++ b/mozglue/linker/moz.build
@@ -28,8 +28,10 @@ DEFINES['IMPL_MFBT'] = True
 
 DISABLE_STL_WRAPPING = True
 
 TEST_DIRS += ['tests']
 
 HOST_OS_LIBS += [
     'z',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5756,17 +5756,20 @@ nsHttpChannel::OnTransportStatus(nsITran
         if (!(mLoadFlags & LOAD_BACKGROUND)) {
             nsAutoCString host;
             mURI->GetHost(host);
             mProgressSink->OnStatus(this, nullptr, status,
                                     NS_ConvertUTF8toUTF16(host).get());
         }
 
         if (progress > 0) {
-            MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
+            if (progress > progressMax) {
+                NS_WARNING("unexpected progress values");
+            }
+
             // Try to get mProgressSink if it was nulled out during OnStatus.
             if (!mProgressSink) {
                 GetCallback(mProgressSink);
             }
             if (mProgressSink) {
                 mProgressSink->OnProgress(this, nullptr, progress, progressMax);
             }
         }
--- a/parser/xml/moz.build
+++ b/parser/xml/moz.build
@@ -31,8 +31,10 @@ SOURCES += [
     'nsSAXAttributes.cpp',
     'nsSAXLocator.cpp',
     'nsSAXXMLReader.cpp',
 ]
 
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/python/mach/README.rst
+++ b/python/mach/README.rst
@@ -5,324 +5,9 @@ mach
 Mach (German for *do*) is a generic command dispatcher for the command
 line.
 
 To use mach, you install the mach core (a Python package), create an
 executable *driver* script (named whatever you want), and write mach
 commands. When the *driver* is executed, mach dispatches to the
 requested command handler automatically.
 
-Features
-========
-
-On a high level, mach is similar to using argparse with subparsers (for
-command handling). When you dig deeper, mach offers a number of
-additional features:
-
-Distributed command definitions
-  With optparse/argparse, you have to define your commands on a central
-  parser instance. With mach, you annotate your command methods with
-  decorators and mach finds and dispatches to them automatically.
-
-Command categories
-  Mach commands can be grouped into categories when displayed in help.
-  This is currently not possible with argparse.
-
-Logging management
-  Mach provides a facility for logging (both classical text and
-  structured) that is available to any command handler.
-
-Settings files
-  Mach provides a facility for reading settings from an ini-like file
-  format.
-
-Components
-==========
-
-Mach is conceptually composed of the following components:
-
-core
-  The mach core is the core code powering mach. This is a Python package
-  that contains all the business logic that makes mach work. The mach
-  core is common to all mach deployments.
-
-commands
-  These are what mach dispatches to. Commands are simply Python methods
-  registered as command names. The set of commands is unique to the
-  environment mach is deployed in.
-
-driver
-  The *driver* is the entry-point to mach. It is simply an executable
-  script that loads the mach core, tells it where commands can be found,
-  then asks the mach core to handle the current request. The driver is
-  unique to the deployed environment. But, it's usually based on an
-  example from this source tree.
-
-Project State
-=============
-
-mach was originally written as a command dispatching framework to aid
-Firefox development. While the code is mostly generic, there are still
-some pieces that closely tie it to Mozilla/Firefox. The goal is for
-these to eventually be removed and replaced with generic features so
-mach is suitable for anybody to use. Until then, mach may not be the
-best fit for you.
-
-Implementing Commands
----------------------
-
-Mach commands are defined via Python decorators.
-
-All the relevant decorators are defined in the *mach.decorators* module.
-The important decorators are as follows:
-
-CommandProvider
-  A class decorator that denotes that a class contains mach
-  commands. The decorator takes no arguments.
-
-Command
-  A method decorator that denotes that the method should be called when
-  the specified command is requested. The decorator takes a command name
-  as its first argument and a number of additional arguments to
-  configure the behavior of the command.
-
-CommandArgument
-  A method decorator that defines an argument to the command. Its
-  arguments are essentially proxied to ArgumentParser.add_argument()
-
-Classes with the *@CommandProvider* decorator *must* have an *__init__*
-method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
-2nd argument will be a *MachCommandContext* instance. This is just a named
-tuple containing references to objects provided by the mach driver.
-
-Here is a complete example::
-
-    from mach.decorators import (
-        CommandArgument,
-        CommandProvider,
-        Command,
-    )
-
-    @CommandProvider
-    class MyClass(object):
-        @Command('doit', help='Do ALL OF THE THINGS.')
-        @CommandArgument('--force', '-f', action='store_true',
-            help='Force doing it.')
-        def doit(self, force=False):
-            # Do stuff here.
-
-When the module is loaded, the decorators tell mach about all handlers.
-When mach runs, it takes the assembled metadata from these handlers and
-hooks it up to the command line driver. Under the hood, arguments passed
-to the decorators are being used to help mach parse command arguments,
-formulate arguments to the methods, etc. See the documentation in the
-*mach.base* module for more.
-
-The Python modules defining mach commands do not need to live inside the
-main mach source tree.
-
-Conditionally Filtering Commands
---------------------------------
-
-Sometimes it might only make sense to run a command given a certain
-context. For example, running tests only makes sense if the product
-they are testing has been built, and said build is available. To make
-sure a command is only runnable from within a correct context, you can
-define a series of conditions on the *Command* decorator.
-
-A condition is simply a function that takes an instance of the
-*CommandProvider* class as an argument, and returns True or False. If
-any of the conditions defined on a command return False, the command
-will not be runnable. The doc string of a condition function is used in
-error messages, to explain why the command cannot currently be run.
-
-Here is an example:
-
-    from mach.decorators import (
-        CommandProvider,
-        Command,
-    )
-
-    def build_available(cls):
-        """The build needs to be available."""
-        return cls.build_path is not None
-
-    @CommandProvider
-    class MyClass(MachCommandBase):
-        def __init__(self, build_path=None):
-            self.build_path = build_path
-
-        @Command('run_tests', conditions=[build_available])
-        def run_tests(self):
-            # Do stuff here.
-
-It is important to make sure that any state needed by the condition is
-available to instances of the command provider.
-
-By default all commands without any conditions applied will be runnable,
-but it is possible to change this behaviour by setting *require_conditions*
-to True:
-
-    m = mach.main.Mach()
-    m.require_conditions = True
-
-Minimizing Code in Commands
----------------------------
-
-Mach command modules, classes, and methods work best when they are
-minimal dispatchers. The reason is import bloat. Currently, the mach
-core needs to import every Python file potentially containing mach
-commands for every command invocation. If you have dozens of commands or
-commands in modules that import a lot of Python code, these imports
-could slow mach down and waste memory.
-
-It is thus recommended that mach modules, classes, and methods do as
-little work as possible. Ideally the module should only import from
-the *mach* package. If you need external modules, you should import them
-from within the command method.
-
-To keep code size small, the body of a command method should be limited
-to:
-
-1. Obtaining user input (parsing arguments, prompting, etc)
-2. Calling into some other Python package
-3. Formatting output
-
-Of course, these recommendations can be ignored if you want to risk
-slower performance.
-
-In the future, the mach driver may cache the dispatching information or
-have it intelligently loaded to facilitate lazy loading.
-
-Logging
-=======
-
-Mach configures a built-in logging facility so commands can easily log
-data.
-
-What sets the logging facility apart from most loggers you've seen is
-that it encourages structured logging. Instead of conventional logging
-where simple strings are logged, the internal logging mechanism logs all
-events with the following pieces of information:
-
-* A string *action*
-* A dict of log message fields
-* A formatting string
-
-Essentially, instead of assembling a human-readable string at
-logging-time, you create an object holding all the pieces of data that
-will constitute your logged event. For each unique type of logged event,
-you assign an *action* name.
-
-Depending on how logging is configured, your logged event could get
-written a couple of different ways.
-
-JSON Logging
-------------
-
-Where machines are the intended target of the logging data, a JSON
-logger is configured. The JSON logger assembles an array consisting of
-the following elements:
-
-* Decimal wall clock time in seconds since UNIX epoch
-* String *action* of message
-* Object with structured message data
-
-The JSON-serialized array is written to a configured file handle.
-Consumers of this logging stream can just perform a readline() then feed
-that into a JSON deserializer to reconstruct the original logged
-message. They can key off the *action* element to determine how to
-process individual events. There is no need to invent a parser.
-Convenient, isn't it?
-
-Logging for Humans
-------------------
-
-Where humans are the intended consumer of a log message, the structured
-log message are converted to more human-friendly form. This is done by
-utilizing the *formatting* string provided at log time. The logger
-simply calls the *format* method of the formatting string, passing the
-dict containing the message's fields.
-
-When *mach* is used in a terminal that supports it, the logging facility
-also supports terminal features such as colorization. This is done
-automatically in the logging layer - there is no need to control this at
-logging time.
-
-In addition, messages intended for humans typically prepends every line
-with the time passed since the application started.
-
-Logging HOWTO
--------------
-
-Structured logging piggybacks on top of Python's built-in logging
-infrastructure provided by the *logging* package. We accomplish this by
-taking advantage of *logging.Logger.log()*'s *extra* argument. To this
-argument, we pass a dict with the fields *action* and *params*. These
-are the string *action* and dict of message fields, respectively. The
-formatting string is passed as the *msg* argument, like normal.
-
-If you were logging to a logger directly, you would do something like:
-
-    logger.log(logging.INFO, 'My name is {name}',
-        extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
-
-The JSON logging would produce something like:
-
-    [1339985554.306338, "my_name", {"name": "Gregory"}]
-
-Human logging would produce something like:
-
-     0.52 My name is Gregory
-
-Since there is a lot of complexity using logger.log directly, it is
-recommended to go through a wrapping layer that hides part of the
-complexity for you. The easiest way to do this is by utilizing the
-LoggingMixin:
-
-    import logging
-    from mach.mixin.logging import LoggingMixin
-
-    class MyClass(LoggingMixin):
-        def foo(self):
-             self.log(logging.INFO, 'foo_start', {'bar': True},
-                 'Foo performed. Bar: {bar}')
-
-Entry Points
-============
-
-It is possible to use setuptools' entry points to load commands
-directly from python packages. A mach entry point is a function which
-returns a list of files or directories containing mach command
-providers. e.g.::
-
-    def list_providers():
-        providers = []
-        here = os.path.abspath(os.path.dirname(__file__))
-        for p in os.listdir(here):
-            if p.endswith('.py'):
-                providers.append(os.path.join(here, p))
-        return providers
-
-See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
-for more information on creating an entry point. To search for entry
-point plugins, you can call *load_commands_from_entry_point*. This
-takes a single parameter called *group*. This is the name of the entry
-point group to load and defaults to ``mach.providers``. e.g.::
-
-    mach.load_commands_from_entry_point("mach.external.providers")
-
-Adding Global Arguments
-=======================
-
-Arguments to mach commands are usually command-specific. However,
-mach ships with a handful of global arguments that apply to all
-commands.
-
-It is possible to extend the list of global arguments. In your
-*mach driver*, simply call ``add_global_argument()`` on your
-``mach.main.Mach`` instance. e.g.::
-
-   mach = mach.main.Mach(os.getcwd())
-
-   # Will allow --example to be specified on every mach command.
-   mach.add_global_argument('--example', action='store_true',
-       help='Demonstrate an example global argument.')
+To learn more, read the docs in ``docs/``.
copy from python/mach/README.rst
copy to python/mach/docs/commands.rst
--- a/python/mach/README.rst
+++ b/python/mach/docs/commands.rst
@@ -1,328 +1,145 @@
-====
-mach
-====
-
-Mach (German for *do*) is a generic command dispatcher for the command
-line.
-
-To use mach, you install the mach core (a Python package), create an
-executable *driver* script (named whatever you want), and write mach
-commands. When the *driver* is executed, mach dispatches to the
-requested command handler automatically.
-
-Features
-========
-
-On a high level, mach is similar to using argparse with subparsers (for
-command handling). When you dig deeper, mach offers a number of
-additional features:
-
-Distributed command definitions
-  With optparse/argparse, you have to define your commands on a central
-  parser instance. With mach, you annotate your command methods with
-  decorators and mach finds and dispatches to them automatically.
-
-Command categories
-  Mach commands can be grouped into categories when displayed in help.
-  This is currently not possible with argparse.
-
-Logging management
-  Mach provides a facility for logging (both classical text and
-  structured) that is available to any command handler.
+.. _mach_commands:
 
-Settings files
-  Mach provides a facility for reading settings from an ini-like file
-  format.
-
-Components
-==========
-
-Mach is conceptually composed of the following components:
-
-core
-  The mach core is the core code powering mach. This is a Python package
-  that contains all the business logic that makes mach work. The mach
-  core is common to all mach deployments.
-
-commands
-  These are what mach dispatches to. Commands are simply Python methods
-  registered as command names. The set of commands is unique to the
-  environment mach is deployed in.
-
-driver
-  The *driver* is the entry-point to mach. It is simply an executable
-  script that loads the mach core, tells it where commands can be found,
-  then asks the mach core to handle the current request. The driver is
-  unique to the deployed environment. But, it's usually based on an
-  example from this source tree.
-
-Project State
-=============
-
-mach was originally written as a command dispatching framework to aid
-Firefox development. While the code is mostly generic, there are still
-some pieces that closely tie it to Mozilla/Firefox. The goal is for
-these to eventually be removed and replaced with generic features so
-mach is suitable for anybody to use. Until then, mach may not be the
-best fit for you.
-
+=====================
 Implementing Commands
----------------------
+=====================
 
 Mach commands are defined via Python decorators.
 
 All the relevant decorators are defined in the *mach.decorators* module.
 The important decorators are as follows:
 
-CommandProvider
+:py:func:`CommandProvider <mach.decorators.CommandProvider>`
   A class decorator that denotes that a class contains mach
   commands. The decorator takes no arguments.
 
-Command
+:py:func:`Command <mach.decorators.Command>`
   A method decorator that denotes that the method should be called when
   the specified command is requested. The decorator takes a command name
   as its first argument and a number of additional arguments to
   configure the behavior of the command.
 
-CommandArgument
+:py:func:`CommandArgument <mach.decorators.CommandArgument>`
   A method decorator that defines an argument to the command. Its
   arguments are essentially proxied to ArgumentParser.add_argument()
 
-Classes with the *@CommandProvider* decorator *must* have an *__init__*
-method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
-2nd argument will be a *MachCommandContext* instance. This is just a named
-tuple containing references to objects provided by the mach driver.
+:py:func:`SubCommand <mach.decorators.SubCommand`
+  A method decorator that denotes that the method should be a
+  sub-command to an existing ``@Command``. The decorator takes the
+  parent command name as its first argument and the sub-command name
+  as its second argument.
 
-Here is a complete example::
+  ``@CommandArgument`` can be used on ``@SubCommand`` instances just
+  like they can on ``@Command`` instances.
+
+Classes with the ``@CommandProvider`` decorator **must** have an
+``__init__`` method that accepts 1 or 2 arguments. If it accepts 2
+arguments, the 2nd argument will be a
+:py:class:`mach.base.CommandContext` instance.
+
+Here is a complete example:
 
-    from mach.decorators import (
-        CommandArgument,
-        CommandProvider,
-        Command,
-    )
+.. code-block:: python
+
+   from mach.decorators import (
+       CommandArgument,
+       CommandProvider,
+       Command,
+   )
 
-    @CommandProvider
-    class MyClass(object):
-        @Command('doit', help='Do ALL OF THE THINGS.')
-        @CommandArgument('--force', '-f', action='store_true',
-            help='Force doing it.')
-        def doit(self, force=False):
-            # Do stuff here.
+   @CommandProvider
+   class MyClass(object):
+       @Command('doit', help='Do ALL OF THE THINGS.')
+       @CommandArgument('--force', '-f', action='store_true',
+           help='Force doing it.')
+       def doit(self, force=False):
+           # Do stuff here.
 
 When the module is loaded, the decorators tell mach about all handlers.
 When mach runs, it takes the assembled metadata from these handlers and
 hooks it up to the command line driver. Under the hood, arguments passed
 to the decorators are being used to help mach parse command arguments,
 formulate arguments to the methods, etc. See the documentation in the
-*mach.base* module for more.
+:py:mod:`mach.base` module for more.
 
 The Python modules defining mach commands do not need to live inside the
 main mach source tree.
 
 Conditionally Filtering Commands
---------------------------------
+================================
 
 Sometimes it might only make sense to run a command given a certain
 context. For example, running tests only makes sense if the product
 they are testing has been built, and said build is available. To make
 sure a command is only runnable from within a correct context, you can
-define a series of conditions on the *Command* decorator.
+define a series of conditions on the
+:py:func:`Command <mach.decorators.Command>` decorator.
 
 A condition is simply a function that takes an instance of the
-*CommandProvider* class as an argument, and returns True or False. If
-any of the conditions defined on a command return False, the command
-will not be runnable. The doc string of a condition function is used in
-error messages, to explain why the command cannot currently be run.
+:py:func:`mach.decorators.CommandProvider` class as an argument, and
+returns ``True`` or ``False``. If any of the conditions defined on a
+command return ``False``, the command will not be runnable. The
+docstring of a condition function is used in error messages, to explain
+why the command cannot currently be run.
 
 Here is an example:
 
-    from mach.decorators import (
-        CommandProvider,
-        Command,
-    )
+.. code-block:: python
 
-    def build_available(cls):
-        """The build needs to be available."""
-        return cls.build_path is not None
+   from mach.decorators import (
+       CommandProvider,
+       Command,
+   )
+
+   def build_available(cls):
+       """The build needs to be available."""
+       return cls.build_path is not None
 
     @CommandProvider
-    class MyClass(MachCommandBase):
-        def __init__(self, build_path=None):
-            self.build_path = build_path
+   class MyClass(MachCommandBase):
+       def __init__(self, build_path=None):
+           self.build_path = build_path
 
-        @Command('run_tests', conditions=[build_available])
-        def run_tests(self):
-            # Do stuff here.
+       @Command('run_tests', conditions=[build_available])
+       def run_tests(self):
+           # Do stuff here.
 
 It is important to make sure that any state needed by the condition is
 available to instances of the command provider.
 
 By default all commands without any conditions applied will be runnable,
-but it is possible to change this behaviour by setting *require_conditions*
-to True:
+but it is possible to change this behaviour by setting
+``require_conditions`` to ``True``:
 
-    m = mach.main.Mach()
-    m.require_conditions = True
+.. code-block:: python
+
+   m = mach.main.Mach()
+   m.require_conditions = True
 
 Minimizing Code in Commands
----------------------------
+===========================
 
 Mach command modules, classes, and methods work best when they are
 minimal dispatchers. The reason is import bloat. Currently, the mach
 core needs to import every Python file potentially containing mach
 commands for every command invocation. If you have dozens of commands or
 commands in modules that import a lot of Python code, these imports
 could slow mach down and waste memory.
 
 It is thus recommended that mach modules, classes, and methods do as
 little work as possible. Ideally the module should only import from
-the *mach* package. If you need external modules, you should import them
-from within the command method.
+the :py:mod:`mach` package. If you need external modules, you should
+import them from within the command method.
 
 To keep code size small, the body of a command method should be limited
 to:
 
 1. Obtaining user input (parsing arguments, prompting, etc)
 2. Calling into some other Python package
 3. Formatting output
 
 Of course, these recommendations can be ignored if you want to risk
 slower performance.
 
 In the future, the mach driver may cache the dispatching information or
 have it intelligently loaded to facilitate lazy loading.
-
-Logging
-=======
-
-Mach configures a built-in logging facility so commands can easily log
-data.
-
-What sets the logging facility apart from most loggers you've seen is
-that it encourages structured logging. Instead of conventional logging
-where simple strings are logged, the internal logging mechanism logs all
-events with the following pieces of information:
-
-* A string *action*
-* A dict of log message fields
-* A formatting string
-
-Essentially, instead of assembling a human-readable string at
-logging-time, you create an object holding all the pieces of data that
-will constitute your logged event. For each unique type of logged event,
-you assign an *action* name.
-
-Depending on how logging is configured, your logged event could get
-written a couple of different ways.
-
-JSON Logging
-------------
-
-Where machines are the intended target of the logging data, a JSON
-logger is configured. The JSON logger assembles an array consisting of
-the following elements:
-
-* Decimal wall clock time in seconds since UNIX epoch
-* String *action* of message
-* Object with structured message data
-
-The JSON-serialized array is written to a configured file handle.
-Consumers of this logging stream can just perform a readline() then feed
-that into a JSON deserializer to reconstruct the original logged
-message. They can key off the *action* element to determine how to
-process individual events. There is no need to invent a parser.
-Convenient, isn't it?
-
-Logging for Humans
-------------------
-
-Where humans are the intended consumer of a log message, the structured
-log message are converted to more human-friendly form. This is done by
-utilizing the *formatting* string provided at log time. The logger
-simply calls the *format* method of the formatting string, passing the
-dict containing the message's fields.
-
-When *mach* is used in a terminal that supports it, the logging facility
-also supports terminal features such as colorization. This is done
-automatically in the logging layer - there is no need to control this at
-logging time.
-
-In addition, messages intended for humans typically prepends every line
-with the time passed since the application started.
-
-Logging HOWTO
--------------
-
-Structured logging piggybacks on top of Python's built-in logging
-infrastructure provided by the *logging* package. We accomplish this by
-taking advantage of *logging.Logger.log()*'s *extra* argument. To this
-argument, we pass a dict with the fields *action* and *params*. These
-are the string *action* and dict of message fields, respectively. The
-formatting string is passed as the *msg* argument, like normal.
-
-If you were logging to a logger directly, you would do something like:
-
-    logger.log(logging.INFO, 'My name is {name}',
-        extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
-
-The JSON logging would produce something like:
-
-    [1339985554.306338, "my_name", {"name": "Gregory"}]
-
-Human logging would produce something like:
-
-     0.52 My name is Gregory
-
-Since there is a lot of complexity using logger.log directly, it is
-recommended to go through a wrapping layer that hides part of the
-complexity for you. The easiest way to do this is by utilizing the
-LoggingMixin:
-
-    import logging
-    from mach.mixin.logging import LoggingMixin
-
-    class MyClass(LoggingMixin):
-        def foo(self):
-             self.log(logging.INFO, 'foo_start', {'bar': True},
-                 'Foo performed. Bar: {bar}')
-
-Entry Points
-============
-
-It is possible to use setuptools' entry points to load commands
-directly from python packages. A mach entry point is a function which
-returns a list of files or directories containing mach command
-providers. e.g.::
-
-    def list_providers():
-        providers = []
-        here = os.path.abspath(os.path.dirname(__file__))
-        for p in os.listdir(here):
-            if p.endswith('.py'):
-                providers.append(os.path.join(here, p))
-        return providers
-
-See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
-for more information on creating an entry point. To search for entry
-point plugins, you can call *load_commands_from_entry_point*. This
-takes a single parameter called *group*. This is the name of the entry
-point group to load and defaults to ``mach.providers``. e.g.::
-
-    mach.load_commands_from_entry_point("mach.external.providers")
-
-Adding Global Arguments
-=======================
-
-Arguments to mach commands are usually command-specific. However,
-mach ships with a handful of global arguments that apply to all
-commands.
-
-It is possible to extend the list of global arguments. In your
-*mach driver*, simply call ``add_global_argument()`` on your
-``mach.main.Mach`` instance. e.g.::
-
-   mach = mach.main.Mach(os.getcwd())
-
-   # Will allow --example to be specified on every mach command.
-   mach.add_global_argument('--example', action='store_true',
-       help='Demonstrate an example global argument.')
copy from python/mach/README.rst
copy to python/mach/docs/driver.rst
--- a/python/mach/README.rst
+++ b/python/mach/docs/driver.rst
@@ -1,328 +1,51 @@
-====
-mach
-====
-
-Mach (German for *do*) is a generic command dispatcher for the command
-line.
-
-To use mach, you install the mach core (a Python package), create an
-executable *driver* script (named whatever you want), and write mach
-commands. When the *driver* is executed, mach dispatches to the
-requested command handler automatically.
-
-Features
-========
-
-On a high level, mach is similar to using argparse with subparsers (for
-command handling). When you dig deeper, mach offers a number of
-additional features:
-
-Distributed command definitions
-  With optparse/argparse, you have to define your commands on a central
-  parser instance. With mach, you annotate your command methods with
-  decorators and mach finds and dispatches to them automatically.
-
-Command categories
-  Mach commands can be grouped into categories when displayed in help.
-  This is currently not possible with argparse.
-
-Logging management
-  Mach provides a facility for logging (both classical text and
-  structured) that is available to any command handler.
-
-Settings files
-  Mach provides a facility for reading settings from an ini-like file
-  format.
-
-Components
-==========
-
-Mach is conceptually composed of the following components:
-
-core
-  The mach core is the core code powering mach. This is a Python package
-  that contains all the business logic that makes mach work. The mach
-  core is common to all mach deployments.
-
-commands
-  These are what mach dispatches to. Commands are simply Python methods
-  registered as command names. The set of commands is unique to the
-  environment mach is deployed in.
-
-driver
-  The *driver* is the entry-point to mach. It is simply an executable
-  script that loads the mach core, tells it where commands can be found,
-  then asks the mach core to handle the current request. The driver is
-  unique to the deployed environment. But, it's usually based on an
-  example from this source tree.
-
-Project State
-=============
-
-mach was originally written as a command dispatching framework to aid
-Firefox development. While the code is mostly generic, there are still
-some pieces that closely tie it to Mozilla/Firefox. The goal is for
-these to eventually be removed and replaced with generic features so
-mach is suitable for anybody to use. Until then, mach may not be the
-best fit for you.
-
-Implementing Commands
----------------------
-
-Mach commands are defined via Python decorators.
-
-All the relevant decorators are defined in the *mach.decorators* module.
-The important decorators are as follows:
-
-CommandProvider
-  A class decorator that denotes that a class contains mach
-  commands. The decorator takes no arguments.
-
-Command
-  A method decorator that denotes that the method should be called when
-  the specified command is requested. The decorator takes a command name
-  as its first argument and a number of additional arguments to
-  configure the behavior of the command.
-
-CommandArgument
-  A method decorator that defines an argument to the command. Its
-  arguments are essentially proxied to ArgumentParser.add_argument()
-
-Classes with the *@CommandProvider* decorator *must* have an *__init__*
-method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
-2nd argument will be a *MachCommandContext* instance. This is just a named
-tuple containing references to objects provided by the mach driver.
-
-Here is a complete example::
-
-    from mach.decorators import (
-        CommandArgument,
-        CommandProvider,
-        Command,
-    )
-
-    @CommandProvider
-    class MyClass(object):
-        @Command('doit', help='Do ALL OF THE THINGS.')
-        @CommandArgument('--force', '-f', action='store_true',
-            help='Force doing it.')
-        def doit(self, force=False):
-            # Do stuff here.
-
-When the module is loaded, the decorators tell mach about all handlers.
-When mach runs, it takes the assembled metadata from these handlers and
-hooks it up to the command line driver. Under the hood, arguments passed
-to the decorators are being used to help mach parse command arguments,
-formulate arguments to the methods, etc. See the documentation in the
-*mach.base* module for more.
-
-The Python modules defining mach commands do not need to live inside the
-main mach source tree.
-
-Conditionally Filtering Commands
---------------------------------
-
-Sometimes it might only make sense to run a command given a certain
-context. For example, running tests only makes sense if the product
-they are testing has been built, and said build is available. To make
-sure a command is only runnable from within a correct context, you can
-define a series of conditions on the *Command* decorator.
-
-A condition is simply a function that takes an instance of the
-*CommandProvider* class as an argument, and returns True or False. If
-any of the conditions defined on a command return False, the command
-will not be runnable. The doc string of a condition function is used in
-error messages, to explain why the command cannot currently be run.
-
-Here is an example:
-
-    from mach.decorators import (
-        CommandProvider,
-        Command,
-    )
+.. _mach_driver:
 
-    def build_available(cls):
-        """The build needs to be available."""
-        return cls.build_path is not None
-
-    @CommandProvider
-    class MyClass(MachCommandBase):
-        def __init__(self, build_path=None):
-            self.build_path = build_path
-
-        @Command('run_tests', conditions=[build_available])
-        def run_tests(self):
-            # Do stuff here.
-
-It is important to make sure that any state needed by the condition is
-available to instances of the command provider.
-
-By default all commands without any conditions applied will be runnable,
-but it is possible to change this behaviour by setting *require_conditions*
-to True:
-
-    m = mach.main.Mach()
-    m.require_conditions = True
-
-Minimizing Code in Commands
----------------------------
-
-Mach command modules, classes, and methods work best when they are
-minimal dispatchers. The reason is import bloat. Currently, the mach
-core needs to import every Python file potentially containing mach
-commands for every command invocation. If you have dozens of commands or
-commands in modules that import a lot of Python code, these imports
-could slow mach down and waste memory.
-
-It is thus recommended that mach modules, classes, and methods do as
-little work as possible. Ideally the module should only import from
-the *mach* package. If you need external modules, you should import them
-from within the command method.
-
-To keep code size small, the body of a command method should be limited
-to:
-
-1. Obtaining user input (parsing arguments, prompting, etc)
-2. Calling into some other Python package
-3. Formatting output
-
-Of course, these recommendations can be ignored if you want to risk
-slower performance.
-
-In the future, the mach driver may cache the dispatching information or
-have it intelligently loaded to facilitate lazy loading.
-
-Logging
 =======
-
-Mach configures a built-in logging facility so commands can easily log
-data.
-
-What sets the logging facility apart from most loggers you've seen is
-that it encourages structured logging. Instead of conventional logging
-where simple strings are logged, the internal logging mechanism logs all
-events with the following pieces of information:
-
-* A string *action*
-* A dict of log message fields
-* A formatting string
-
-Essentially, instead of assembling a human-readable string at
-logging-time, you create an object holding all the pieces of data that
-will constitute your logged event. For each unique type of logged event,
-you assign an *action* name.
-
-Depending on how logging is configured, your logged event could get
-written a couple of different ways.
-
-JSON Logging
-------------
-
-Where machines are the intended target of the logging data, a JSON
-logger is configured. The JSON logger assembles an array consisting of
-the following elements:
-
-* Decimal wall clock time in seconds since UNIX epoch
-* String *action* of message
-* Object with structured message data
-
-The JSON-serialized array is written to a configured file handle.
-Consumers of this logging stream can just perform a readline() then feed
-that into a JSON deserializer to reconstruct the original logged
-message. They can key off the *action* element to determine how to
-process individual events. There is no need to invent a parser.
-Convenient, isn't it?
-
-Logging for Humans
-------------------
-
-Where humans are the intended consumer of a log message, the structured
-log message are converted to more human-friendly form. This is done by
-utilizing the *formatting* string provided at log time. The logger
-simply calls the *format* method of the formatting string, passing the
-dict containing the message's fields.
-
-When *mach* is used in a terminal that supports it, the logging facility
-also supports terminal features such as colorization. This is done
-automatically in the logging layer - there is no need to control this at
-logging time.
-
-In addition, messages intended for humans typically prepends every line
-with the time passed since the application started.
-
-Logging HOWTO
--------------
-
-Structured logging piggybacks on top of Python's built-in logging
-infrastructure provided by the *logging* package. We accomplish this by
-taking advantage of *logging.Logger.log()*'s *extra* argument. To this
-argument, we pass a dict with the fields *action* and *params*. These
-are the string *action* and dict of message fields, respectively. The
-formatting string is passed as the *msg* argument, like normal.
-
-If you were logging to a logger directly, you would do something like:
-
-    logger.log(logging.INFO, 'My name is {name}',
-        extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
-
-The JSON logging would produce something like:
-
-    [1339985554.306338, "my_name", {"name": "Gregory"}]
-
-Human logging would produce something like:
-
-     0.52 My name is Gregory
-
-Since there is a lot of complexity using logger.log directly, it is
-recommended to go through a wrapping layer that hides part of the
-complexity for you. The easiest way to do this is by utilizing the
-LoggingMixin:
-
-    import logging
-    from mach.mixin.logging import LoggingMixin
-
-    class MyClass(LoggingMixin):
-        def foo(self):
-             self.log(logging.INFO, 'foo_start', {'bar': True},
-                 'Foo performed. Bar: {bar}')
+Drivers
+=======
 
 Entry Points
 ============
 
 It is possible to use setuptools' entry points to load commands
 directly from python packages. A mach entry point is a function which
 returns a list of files or directories containing mach command
-providers. e.g.::
+providers. e.g.:
+
+.. code-block:: python
 
-    def list_providers():
-        providers = []
-        here = os.path.abspath(os.path.dirname(__file__))
-        for p in os.listdir(here):
-            if p.endswith('.py'):
-                providers.append(os.path.join(here, p))
-        return providers
+   def list_providers():
+       providers = []
+       here = os.path.abspath(os.path.dirname(__file__))
+       for p in os.listdir(here):
+           if p.endswith('.py'):
+               providers.append(os.path.join(here, p))
+       return providers
 
 See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
 for more information on creating an entry point. To search for entry
-point plugins, you can call *load_commands_from_entry_point*. This
-takes a single parameter called *group*. This is the name of the entry
-point group to load and defaults to ``mach.providers``. e.g.::
+point plugins, you can call
+:py:meth:`mach.main.Mach.load_commands_from_entry_point`. e.g.:
 
-    mach.load_commands_from_entry_point("mach.external.providers")
+.. code-block:: python
+
+   mach.load_commands_from_entry_point("mach.external.providers")
 
 Adding Global Arguments
 =======================
 
 Arguments to mach commands are usually command-specific. However,
 mach ships with a handful of global arguments that apply to all
 commands.
 
 It is possible to extend the list of global arguments. In your
-*mach driver*, simply call ``add_global_argument()`` on your
-``mach.main.Mach`` instance. e.g.::
+*mach driver*, simply call
+:py:meth:`mach.main.Mach.add_global_argument`. e.g.:
+
+.. code-block:: python
 
    mach = mach.main.Mach(os.getcwd())
 
    # Will allow --example to be specified on every mach command.
    mach.add_global_argument('--example', action='store_true',
        help='Demonstrate an example global argument.')
copy from python/mach/README.rst
copy to python/mach/docs/index.rst
--- a/python/mach/README.rst
+++ b/python/mach/docs/index.rst
@@ -61,268 +61,14 @@ Project State
 
 mach was originally written as a command dispatching framework to aid
 Firefox development. While the code is mostly generic, there are still
 some pieces that closely tie it to Mozilla/Firefox. The goal is for
 these to eventually be removed and replaced with generic features so
 mach is suitable for anybody to use. Until then, mach may not be the
 best fit for you.
 
-Implementing Commands
----------------------
-
-Mach commands are defined via Python decorators.
-
-All the relevant decorators are defined in the *mach.decorators* module.
-The important decorators are as follows:
-
-CommandProvider
-  A class decorator that denotes that a class contains mach
-  commands. The decorator takes no arguments.
-
-Command
-  A method decorator that denotes that the method should be called when
-  the specified command is requested. The decorator takes a command name
-  as its first argument and a number of additional arguments to
-  configure the behavior of the command.
-
-CommandArgument
-  A method decorator that defines an argument to the command. Its
-  arguments are essentially proxied to ArgumentParser.add_argument()
-
-Classes with the *@CommandProvider* decorator *must* have an *__init__*
-method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
-2nd argument will be a *MachCommandContext* instance. This is just a named
-tuple containing references to objects provided by the mach driver.
-
-Here is a complete example::
-
-    from mach.decorators import (
-        CommandArgument,
-        CommandProvider,
-        Command,
-    )
-
-    @CommandProvider
-    class MyClass(object):
-        @Command('doit', help='Do ALL OF THE THINGS.')
-        @CommandArgument('--force', '-f', action='store_true',
-            help='Force doing it.')
-        def doit(self, force=False):
-            # Do stuff here.
-
-When the module is loaded, the decorators tell mach about all handlers.
-When mach runs, it takes the assembled metadata from these handlers and
-hooks it up to the command line driver. Under the hood, arguments passed
-to the decorators are being used to help mach parse command arguments,
-formulate arguments to the methods, etc. See the documentation in the
-*mach.base* module for more.
-
-The Python modules defining mach commands do not need to live inside the
-main mach source tree.
-
-Conditionally Filtering Commands
---------------------------------
-
-Sometimes it might only make sense to run a command given a certain
-context. For example, running tests only makes sense if the product
-they are testing has been built, and said build is available. To make
-sure a command is only runnable from within a correct context, you can
-define a series of conditions on the *Command* decorator.
-
-A condition is simply a function that takes an instance of the
-*CommandProvider* class as an argument, and returns True or False. If
-any of the conditions defined on a command return False, the command
-will not be runnable. The doc string of a condition function is used in
-error messages, to explain why the command cannot currently be run.
-
-Here is an example:
-
-    from mach.decorators import (
-        CommandProvider,
-        Command,
-    )
-
-    def build_available(cls):
-        """The build needs to be available."""
-        return cls.build_path is not None
-
-    @CommandProvider
-    class MyClass(MachCommandBase):
-        def __init__(self, build_path=None):
-            self.build_path = build_path
-
-        @Command('run_tests', conditions=[build_available])
-        def run_tests(self):
-            # Do stuff here.
-
-It is important to make sure that any state needed by the condition is
-available to instances of the command provider.
-
-By default all commands without any conditions applied will be runnable,
-but it is possible to change this behaviour by setting *require_conditions*
-to True:
-
-    m = mach.main.Mach()
-    m.require_conditions = True
-
-Minimizing Code in Commands
----------------------------
-
-Mach command modules, classes, and methods work best when they are
-minimal dispatchers. The reason is import bloat. Currently, the mach
-core needs to import every Python file potentially containing mach
-commands for every command invocation. If you have dozens of commands or
-commands in modules that import a lot of Python code, these imports
-could slow mach down and waste memory.
-
-It is thus recommended that mach modules, classes, and methods do as
-little work as possible. Ideally the module should only import from
-the *mach* package. If you need external modules, you should import them
-from within the command method.
-
-To keep code size small, the body of a command method should be limited
-to:
-
-1. Obtaining user input (parsing arguments, prompting, etc)
-2. Calling into some other Python package
-3. Formatting output
-
-Of course, these recommendations can be ignored if you want to risk
-slower performance.
-
-In the future, the mach driver may cache the dispatching information or
-have it intelligently loaded to facilitate lazy loading.
-
-Logging
-=======
+.. toctree::
+   :maxdepth: 1
 
-Mach configures a built-in logging facility so commands can easily log
-data.
-
-What sets the logging facility apart from most loggers you've seen is
-that it encourages structured logging. Instead of conventional logging
-where simple strings are logged, the internal logging mechanism logs all
-events with the following pieces of information:
-
-* A string *action*
-* A dict of log message fields
-* A formatting string
-
-Essentially, instead of assembling a human-readable string at
-logging-time, you create an object holding all the pieces of data that
-will constitute your logged event. For each unique type of logged event,
-you assign an *action* name.
-
-Depending on how logging is configured, your logged event could get
-written a couple of different ways.
-
-JSON Logging
-------------
-
-Where machines are the intended target of the logging data, a JSON
-logger is configured. The JSON logger assembles an array consisting of
-the following elements:
-
-* Decimal wall clock time in seconds since UNIX epoch
-* String *action* of message
-* Object with structured message data
-
-The JSON-serialized array is written to a configured file handle.
-Consumers of this logging stream can just perform a readline() then feed
-that into a JSON deserializer to reconstruct the original logged
-message. They can key off the *action* element to determine how to
-process individual events. There is no need to invent a parser.
-Convenient, isn't it?
-
-Logging for Humans
-------------------
-
-Where humans are the intended consumer of a log message, the structured
-log message are converted to more human-friendly form. This is done by
-utilizing the *formatting* string provided at log time. The logger
-simply calls the *format* method of the formatting string, passing the
-dict containing the message's fields.
-
-When *mach* is used in a terminal that supports it, the logging facility
-also supports terminal features such as colorization. This is done
-automatically in the logging layer - there is no need to control this at
-logging time.
-
-In addition, messages intended for humans typically prepends every line
-with the time passed since the application started.
-
-Logging HOWTO
--------------
-
-Structured logging piggybacks on top of Python's built-in logging
-infrastructure provided by the *logging* package. We accomplish this by
-taking advantage of *logging.Logger.log()*'s *extra* argument. To this
-argument, we pass a dict with the fields *action* and *params*. These
-are the string *action* and dict of message fields, respectively. The
-formatting string is passed as the *msg* argument, like normal.
-
-If you were logging to a logger directly, you would do something like:
-
-    logger.log(logging.INFO, 'My name is {name}',
-        extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
-
-The JSON logging would produce something like:
-
-    [1339985554.306338, "my_name", {"name": "Gregory"}]
-
-Human logging would produce something like:
-
-     0.52 My name is Gregory
-
-Since there is a lot of complexity using logger.log directly, it is
-recommended to go through a wrapping layer that hides part of the
-complexity for you. The easiest way to do this is by utilizing the
-LoggingMixin:
-
-    import logging
-    from mach.mixin.logging import LoggingMixin
-
-    class MyClass(LoggingMixin):
-        def foo(self):
-             self.log(logging.INFO, 'foo_start', {'bar': True},
-                 'Foo performed. Bar: {bar}')
-
-Entry Points
-============
-
-It is possible to use setuptools' entry points to load commands
-directly from python packages. A mach entry point is a function which
-returns a list of files or directories containing mach command
-providers. e.g.::
-
-    def list_providers():
-        providers = []
-        here = os.path.abspath(os.path.dirname(__file__))
-        for p in os.listdir(here):
-            if p.endswith('.py'):
-                providers.append(os.path.join(here, p))
-        return providers
-
-See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
-for more information on creating an entry point. To search for entry
-point plugins, you can call *load_commands_from_entry_point*. This
-takes a single parameter called *group*. This is the name of the entry
-point group to load and defaults to ``mach.providers``. e.g.::
-
-    mach.load_commands_from_entry_point("mach.external.providers")
-
-Adding Global Arguments
-=======================
-
-Arguments to mach commands are usually command-specific. However,
-mach ships with a handful of global arguments that apply to all
-commands.
-
-It is possible to extend the list of global arguments. In your
-*mach driver*, simply call ``add_global_argument()`` on your
-``mach.main.Mach`` instance. e.g.::
-
-   mach = mach.main.Mach(os.getcwd())
-
-   # Will allow --example to be specified on every mach command.
-   mach.add_global_argument('--example', action='store_true',
-       help='Demonstrate an example global argument.')
+   commands
+   driver
+   logging
copy from python/mach/README.rst
copy to python/mach/docs/logging.rst
--- a/python/mach/README.rst
+++ b/python/mach/docs/logging.rst
@@ -1,202 +1,11 @@
-====
-mach
-====
-
-Mach (German for *do*) is a generic command dispatcher for the command
-line.
-
-To use mach, you install the mach core (a Python package), create an
-executable *driver* script (named whatever you want), and write mach
-commands. When the *driver* is executed, mach dispatches to the
-requested command handler automatically.
-
-Features
-========
-
-On a high level, mach is similar to using argparse with subparsers (for
-command handling). When you dig deeper, mach offers a number of
-additional features:
-
-Distributed command definitions
-  With optparse/argparse, you have to define your commands on a central
-  parser instance. With mach, you annotate your command methods with
-  decorators and mach finds and dispatches to them automatically.
-
-Command categories
-  Mach commands can be grouped into categories when displayed in help.
-  This is currently not possible with argparse.
-
-Logging management
-  Mach provides a facility for logging (both classical text and
-  structured) that is available to any command handler.
-
-Settings files
-  Mach provides a facility for reading settings from an ini-like file
-  format.
-
-Components
-==========
-
-Mach is conceptually composed of the following components:
-
-core
-  The mach core is the core code powering mach. This is a Python package
-  that contains all the business logic that makes mach work. The mach
-  core is common to all mach deployments.
-
-commands
-  These are what mach dispatches to. Commands are simply Python methods
-  registered as command names. The set of commands is unique to the
-  environment mach is deployed in.
-
-driver
-  The *driver* is the entry-point to mach. It is simply an executable
-  script that loads the mach core, tells it where commands can be found,
-  then asks the mach core to handle the current request. The driver is
-  unique to the deployed environment. But, it's usually based on an
-  example from this source tree.
-
-Project State
-=============
-
-mach was originally written as a command dispatching framework to aid
-Firefox development. While the code is mostly generic, there are still
-some pieces that closely tie it to Mozilla/Firefox. The goal is for
-these to eventually be removed and replaced with generic features so
-mach is suitable for anybody to use. Until then, mach may not be the
-best fit for you.
-
-Implementing Commands
----------------------
-
-Mach commands are defined via Python decorators.
-
-All the relevant decorators are defined in the *mach.decorators* module.
-The important decorators are as follows:
-
-CommandProvider
-  A class decorator that denotes that a class contains mach
-  commands. The decorator takes no arguments.
-
-Command
-  A method decorator that denotes that the method should be called when
-  the specified command is requested. The decorator takes a command name
-  as its first argument and a number of additional arguments to
-  configure the behavior of the command.
-
-CommandArgument
-  A method decorator that defines an argument to the command. Its
-  arguments are essentially proxied to ArgumentParser.add_argument()
-
-Classes with the *@CommandProvider* decorator *must* have an *__init__*
-method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
-2nd argument will be a *MachCommandContext* instance. This is just a named
-tuple containing references to objects provided by the mach driver.
-
-Here is a complete example::
+.. _mach_logging:
 
-    from mach.decorators import (
-        CommandArgument,
-        CommandProvider,
-        Command,
-    )
-
-    @CommandProvider
-    class MyClass(object):
-        @Command('doit', help='Do ALL OF THE THINGS.')
-        @CommandArgument('--force', '-f', action='store_true',
-            help='Force doing it.')
-        def doit(self, force=False):
-            # Do stuff here.
-
-When the module is loaded, the decorators tell mach about all handlers.
-When mach runs, it takes the assembled metadata from these handlers and
-hooks it up to the command line driver. Under the hood, arguments passed
-to the decorators are being used to help mach parse command arguments,
-formulate arguments to the methods, etc. See the documentation in the
-*mach.base* module for more.
-
-The Python modules defining mach commands do not need to live inside the
-main mach source tree.
-
-Conditionally Filtering Commands
---------------------------------
-
-Sometimes it might only make sense to run a command given a certain
-context. For example, running tests only makes sense if the product
-they are testing has been built, and said build is available. To make
-sure a command is only runnable from within a correct context, you can
-define a series of conditions on the *Command* decorator.
-
-A condition is simply a function that takes an instance of the
-*CommandProvider* class as an argument, and returns True or False. If
-any of the conditions defined on a command return False, the command
-will not be runnable. The doc string of a condition function is used in
-error messages, to explain why the command cannot currently be run.
-
-Here is an example:
-
-    from mach.decorators import (
-        CommandProvider,
-        Command,
-    )
-
-    def build_available(cls):
-        """The build needs to be available."""
-        return cls.build_path is not None
-
-    @CommandProvider
-    class MyClass(MachCommandBase):
-        def __init__(self, build_path=None):
-            self.build_path = build_path
-
-        @Command('run_tests', conditions=[build_available])
-        def run_tests(self):
-            # Do stuff here.
-
-It is important to make sure that any state needed by the condition is
-available to instances of the command provider.
-
-By default all commands without any conditions applied will be runnable,
-but it is possible to change this behaviour by setting *require_conditions*
-to True:
-
-    m = mach.main.Mach()
-    m.require_conditions = True
-
-Minimizing Code in Commands
----------------------------
-
-Mach command modules, classes, and methods work best when they are
-minimal dispatchers. The reason is import bloat. Currently, the mach
-core needs to import every Python file potentially containing mach
-commands for every command invocation. If you have dozens of commands or
-commands in modules that import a lot of Python code, these imports
-could slow mach down and waste memory.
-
-It is thus recommended that mach modules, classes, and methods do as
-little work as possible. Ideally the module should only import from
-the *mach* package. If you need external modules, you should import them
-from within the command method.
-
-To keep code size small, the body of a command method should be limited
-to:
-
-1. Obtaining user input (parsing arguments, prompting, etc)
-2. Calling into some other Python package
-3. Formatting output
-
-Of course, these recommendations can be ignored if you want to risk
-slower performance.
-
-In the future, the mach driver may cache the dispatching information or
-have it intelligently loaded to facilitate lazy loading.
-
+=======
 Logging
 =======
 
 Mach configures a built-in logging facility so commands can easily log
 data.
 
 What sets the logging facility apart from most loggers you've seen is
 that it encourages structured logging. Instead of conventional logging
@@ -211,17 +20,17 @@ Essentially, instead of assembling a hum
 logging-time, you create an object holding all the pieces of data that
 will constitute your logged event. For each unique type of logged event,
 you assign an *action* name.
 
 Depending on how logging is configured, your logged event could get
 written a couple of different ways.
 
 JSON Logging
-------------
+============
 
 Where machines are the intended target of the logging data, a JSON
 logger is configured. The JSON logger assembles an array consisting of
 the following elements:
 
 * Decimal wall clock time in seconds since UNIX epoch
 * String *action* of message
 * Object with structured message data
@@ -229,100 +38,63 @@ the following elements:
 The JSON-serialized array is written to a configured file handle.
 Consumers of this logging stream can just perform a readline() then feed
 that into a JSON deserializer to reconstruct the original logged
 message. They can key off the *action* element to determine how to
 process individual events. There is no need to invent a parser.
 Convenient, isn't it?
 
 Logging for Humans
-------------------
+==================
 
 Where humans are the intended consumer of a log message, the structured
 log message are converted to more human-friendly form. This is done by
 utilizing the *formatting* string provided at log time. The logger
 simply calls the *format* method of the formatting string, passing the
 dict containing the message's fields.
 
 When *mach* is used in a terminal that supports it, the logging facility
 also supports terminal features such as colorization. This is done
 automatically in the logging layer - there is no need to control this at
 logging time.
 
 In addition, messages intended for humans typically prepends every line
 with the time passed since the application started.
 
 Logging HOWTO
--------------
+=============
 
 Structured logging piggybacks on top of Python's built-in logging
 infrastructure provided by the *logging* package. We accomplish this by
 taking advantage of *logging.Logger.log()*'s *extra* argument. To this
 argument, we pass a dict with the fields *action* and *params*. These
 are the string *action* and dict of message fields, respectively. The
 formatting string is passed as the *msg* argument, like normal.
 
 If you were logging to a logger directly, you would do something like:
 
-    logger.log(logging.INFO, 'My name is {name}',
-        extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
+.. code-block:: python
 
-The JSON logging would produce something like:
+   logger.log(logging.INFO, 'My name is {name}',
+       extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
 
-    [1339985554.306338, "my_name", {"name": "Gregory"}]
+The JSON logging would produce something like::
+
+   [1339985554.306338, "my_name", {"name": "Gregory"}]
 
-Human logging would produce something like:
+Human logging would produce something like::
 
-     0.52 My name is Gregory
+   0.52 My name is Gregory
 
 Since there is a lot of complexity using logger.log directly, it is
 recommended to go through a wrapping layer that hides part of the
 complexity for you. The easiest way to do this is by utilizing the
 LoggingMixin:
 
-    import logging
-    from mach.mixin.logging import LoggingMixin
-
-    class MyClass(LoggingMixin):
-        def foo(self):
-             self.log(logging.INFO, 'foo_start', {'bar': True},
-                 'Foo performed. Bar: {bar}')
-
-Entry Points
-============
+.. code-block:: python
 
-It is possible to use setuptools' entry points to load commands
-directly from python packages. A mach entry point is a function which
-returns a list of files or directories containing mach command
-providers. e.g.::
-
-    def list_providers():
-        providers = []
-        here = os.path.abspath(os.path.dirname(__file__))
-        for p in os.listdir(here):
-            if p.endswith('.py'):
-                providers.append(os.path.join(here, p))
-        return providers
+   import logging
+   from mach.mixin.logging import LoggingMixin
 
-See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
-for more information on creating an entry point. To search for entry
-point plugins, you can call *load_commands_from_entry_point*. This
-takes a single parameter called *group*. This is the name of the entry
-point group to load and defaults to ``mach.providers``. e.g.::
-
-    mach.load_commands_from_entry_point("mach.external.providers")
-
-Adding Global Arguments
-=======================
-
-Arguments to mach commands are usually command-specific. However,
-mach ships with a handful of global arguments that apply to all
-commands.
-
-It is possible to extend the list of global arguments. In your
-*mach driver*, simply call ``add_global_argument()`` on your
-``mach.main.Mach`` instance. e.g.::
-
-   mach = mach.main.Mach(os.getcwd())
-
-   # Will allow --example to be specified on every mach command.
-   mach.add_global_argument('--example', action='store_true',
-       help='Demonstrate an example global argument.')
+   class MyClass(LoggingMixin):
+       def foo(self):
+            self.log(logging.INFO, 'foo_start', {'bar': True},
+                'Foo performed. Bar: {bar}')
--- a/python/mach/mach/base.py
+++ b/python/mach/mach/base.py
@@ -86,25 +86,30 @@ class MethodHandler(object):
         'parser',
 
         # Arguments added to this command's parser. This is a 2-tuple of
         # positional and named arguments, respectively.
         'arguments',
 
         # Argument groups added to this command's parser.
         'argument_group_names',
+
+        # Dict of string to MethodHandler defining sub commands for this
+        # command.
+        'subcommand_handlers',
     )
 
     def __init__(self, cls, method, name, category=None, description=None,
         conditions=None, parser=None, arguments=None,
-        argument_group_names=None, pass_context=False):
+        argument_group_names=None, pass_context=False,
+        subcommand_handlers=None):
 
         self.cls = cls
         self.method = method
         self.name = name
         self.category = category
         self.description = description
         self.conditions = conditions or []
         self.parser = parser
         self.arguments = arguments or []
         self.argument_group_names = argument_group_names or []
         self.pass_context = pass_context
-
+        self.subcommand_handlers = subcommand_handlers or {}
--- a/python/mach/mach/decorators.py
+++ b/python/mach/mach/decorators.py
@@ -86,21 +86,56 @@ def CommandProvider(cls):
 
         handler = MethodHandler(cls, attr, command_name, category=category,
             description=description, conditions=conditions, parser=parser,
             arguments=arguments, argument_group_names=argument_group_names,
             pass_context=pass_context)
 
         Registrar.register_command_handler(handler)
 
+    # Now do another pass to get sub-commands. We do this in two passes so
+    # we can check the parent command existence without having to hold
+    # state and reconcile after traversal.
+    for attr in sorted(cls.__dict__.keys()):
+        value = cls.__dict__[attr]
+
+        if not isinstance(value, types.FunctionType):
+            continue
+
+        command, subcommand, description = getattr(value, '_mach_subcommand',
+            (None, None, None))
+
+        if not command:
+            continue
+
+        if command not in Registrar.command_handlers:
+            raise MachError('Command referenced by sub-command does not '
+                'exist: %s' % command)
+
+        arguments = getattr(value, '_mach_command_args', None)
+        argument_group_names = getattr(value, '_mach_command_arg_group_names', None)
+
+        handler = MethodHandler(cls, attr, subcommand, description=description,
+            arguments=arguments, argument_group_names=argument_group_names,
+            pass_context=pass_context)
+        parent = Registrar.command_handlers[command]
+
+        if parent.parser:
+            raise MachError('cannot declare sub commands against a command '
+                'that has a parser installed: %s' % command)
+        if subcommand in parent.subcommand_handlers:
+            raise MachError('sub-command already defined: %s' % subcommand)
+
+        parent.subcommand_handlers[subcommand] = handler
+
     return cls
 
 
 class Command(object):
-    """Decorator for functions or methods that provide a mach subcommand.
+    """Decorator for functions or methods that provide a mach command.
 
     The decorator accepts arguments that define basic attributes of the
     command. The following arguments are recognized:
 
          category -- The string category to which this command belongs. Mach's
              help will group commands by category.
 
          description -- A brief description of what the command does.
@@ -123,16 +158,43 @@ class Command(object):
         self._parser = parser
 
     def __call__(self, func):
         func._mach_command = (self._name, self._category, self._description,
                               self._conditions, self._parser)
 
         return func
 
+class SubCommand(object):
+    """Decorator for functions or methods that provide a sub-command.
+
+    Mach commands can have sub-commands. e.g. ``mach command foo`` or
+    ``mach command bar``. Each sub-command has its own parser and is
+    effectively its own mach command.
+
+    The decorator accepts arguments that define basic attributes of the
+    sub command:
+
+        command -- The string of the command this sub command should be
+        attached to.
+
+        subcommand -- The string name of the sub command to register.
+
+        description -- A textual description for this sub command.
+    """
+    def __init__(self, command, subcommand, description=None):
+        self._command = command
+        self._subcommand = subcommand
+        self._description = description
+
+    def __call__(self, func):
+        func._mach_subcommand = (self._command, self._subcommand,
+            self._description)
+
+        return func
 
 class CommandArgument(object):
     """Decorator for additional arguments to mach subcommands.
 
     This decorator should be used to add arguments to mach commands. Arguments
     to the decorator are proxied to ArgumentParser.add_argument().
 
     For example:
@@ -157,17 +219,17 @@ class CommandArgument(object):
         command_args.insert(0, self._command_args)
 
         func._mach_command_args = command_args
 
         return func
 
 
 class CommandArgumentGroup(object):
-    """Decorator for additional argument groups to mach subcommands.
+    """Decorator for additional argument groups to mach commands.
 
     This decorator should be used to add arguments groups to mach commands.
     Arguments to the decorator are proxied to
     ArgumentParser.add_argument_group().
 
     For example:
 
         @Command('foo', helps='Run the foo action')
--- a/python/mach/mach/dispatcher.py
+++ b/python/mach/mach/dispatcher.py
@@ -6,16 +6,17 @@ from __future__ import unicode_literals
 
 import argparse
 import difflib
 import sys
 
 from operator import itemgetter
 
 from .base import (
+    MachError,
     NoCommandError,
     UnknownCommandError,
     UnrecognizedArgumentError,
 )
 
 
 class CommandFormatter(argparse.HelpFormatter):
     """Custom formatter to format just a subcommand."""
@@ -90,23 +91,23 @@ class CommandAction(argparse.Action):
             sys.exit(0)
         elif values:
             command = values[0].lower()
             args = values[1:]
 
             if command == 'help':
                 if args and args[0] not in ['-h', '--help']:
                     # Make sure args[0] is indeed a command.
-                    self._handle_subcommand_help(parser, args[0])
+                    self._handle_command_help(parser, args[0])
                 else:
                     self._handle_main_help(parser, namespace.verbose)
                 sys.exit(0)
             elif '-h' in args or '--help' in args:
                 # -h or --help is in the command arguments.
-                self._handle_subcommand_help(parser, command)
+                self._handle_command_help(parser, command)
                 sys.exit(0)
         else:
             raise NoCommandError()
 
         # Command suggestion
         if command not in self._mach_registrar.command_handlers:
             # We first try to look for a valid command that is very similar to the given command.
             suggested_commands = difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.8)
@@ -117,30 +118,60 @@ class CommandAction(argparse.Action):
                 suggested_commands = set(difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.5))
                 suggested_commands |= {cmd for cmd in self._mach_registrar.command_handlers if cmd.startswith(command)}
                 raise UnknownCommandError(command, 'run', suggested_commands)
             sys.stderr.write("We're assuming the '%s' command is '%s' and we're executing it for you.\n\n" % (command, suggested_commands[0]))
             command = suggested_commands[0]
 
         handler = self._mach_registrar.command_handlers.get(command)
 
-        # FUTURE
-        # If we wanted to conditionally enable commands based on whether
-        # it's possible to run them given the current state of system, here
-        # would be a good place to hook that up.
+        usage = '%(prog)s [global arguments] ' + command + \
+            ' [command arguments]'
+
+        subcommand = None
+
+        # If there are sub-commands, parse the intent out immediately.
+        if handler.subcommand_handlers:
+            if not args:
+                self._handle_subcommand_main_help(parser, handler)
+                sys.exit(0)
+            elif len(args) == 1 and args[0] in ('help', '--help'):
+                self._handle_subcommand_main_help(parser, handler)
+                sys.exit(0)
+            # mach <command> help <subcommand>
+            elif len(args) == 2 and args[0] == 'help':
+                subcommand = args[1]
+                subhandler = handler.subcommand_handlers[subcommand]
+                self._handle_subcommand_help(parser, command, subcommand, subhandler)
+                sys.exit(0)
+            # We are running a sub command.
+            else:
+                subcommand = args[0]
+                if subcommand[0] == '-':
+                    raise MachError('%s invoked improperly. A sub-command name '
+                        'must be the first argument after the command name.' %
+                        command)
+
+                if subcommand not in handler.subcommand_handlers:
+                    raise UnknownCommandError(subcommand, 'run',
+                        handler.subcommand_handlers.keys())
+
+                handler = handler.subcommand_handlers[subcommand]
+                usage = '%(prog)s [global arguments] ' + command + ' ' + \
+                    subcommand + ' [command arguments]'
+                args.pop(0)
 
         # We create a new parser, populate it with the command's arguments,
         # then feed all remaining arguments to it, merging the results
         # with ourselves. This is essentially what argparse subparsers
         # do.
 
         parser_args = {
             'add_help': False,
-            'usage': '%(prog)s [global arguments] ' + command +
-                ' [command arguments]',
+            'usage': usage,
         }
 
         if handler.parser:
             subparser = handler.parser
         else:
             subparser = argparse.ArgumentParser(**parser_args)
 
         remainder = None
@@ -161,16 +192,17 @@ class CommandAction(argparse.Action):
                 remainder = arg
             else:
                 subparser.add_argument(*arg[0], **arg[1])
 
         # We define the command information on the main parser result so as to
         # not interfere with arguments passed to the command.
         setattr(namespace, 'mach_handler', handler)
         setattr(namespace, 'command', command)
+        setattr(namespace, 'subcommand', subcommand)
 
         command_namespace, extra = subparser.parse_known_args(args)
         setattr(namespace, 'command_args', command_namespace)
         if remainder:
             (name,), options = remainder
             # parse_known_args usefully puts all arguments after '--' in
             # extra, but also puts '--' there. We don't want to pass it down
             # to the command handler. Note that if multiple '--' are on the
@@ -246,22 +278,41 @@ class CommandAction(argparse.Action):
             group = parser.add_argument_group(title, description)
             if verbose == True:
                 for c in disabled_commands:
                     group.add_argument(c['command'], help=c['description'],
                                        action='store_true')
 
         parser.print_help()
 
-    def _handle_subcommand_help(self, parser, command):
+    def _populate_command_group(self, parser, handler, group):
+        extra_groups = {}
+        for group_name in handler.argument_group_names:
+            group_full_name = 'Command Arguments for ' + group_name
+            extra_groups[group_name] = \
+                parser.add_argument_group(group_full_name)
+
+        for arg in handler.arguments:
+            # Apply our group keyword.
+            group_name = arg[1].get('group')
+            if group_name:
+                del arg[1]['group']
+                group = extra_groups[group_name]
+            group.add_argument(*arg[0], **arg[1])
+
+    def _handle_command_help(self, parser, command):
         handler = self._mach_registrar.command_handlers.get(command)
 
         if not handler:
             raise UnknownCommandError(command, 'query')
 
+        if handler.subcommand_handlers:
+            self._handle_subcommand_main_help(parser, handler)
+            return
+
         # This code is worth explaining. Because we are doing funky things with
         # argument registration to allow the same option in both global and
         # command arguments, we can't simply put all arguments on the same
         # parser instance because argparse would complain. We can't register an
         # argparse subparser here because it won't properly show help for
         # global arguments. So, we employ a strategy similar to command
         # execution where we construct a 2nd, independent ArgumentParser for
         # just the command data then supplement the main help's output with
@@ -288,36 +339,48 @@ class CommandAction(argparse.Action):
 
             if not handler.description:
                 handler.description = c_parser.description
                 c_parser.description = None
         else:
             c_parser = argparse.ArgumentParser(**parser_args)
             group = c_parser.add_argument_group('Command Arguments')
 
-        extra_groups = {}
-        for group_name in handler.argument_group_names:
-            group_full_name = 'Command Arguments for ' + group_name
-            extra_groups[group_name] = \
-                c_parser.add_argument_group(group_full_name)
-
-        for arg in handler.arguments:
-            # Apply our group keyword.
-            group_name = arg[1].get('group')
-            if group_name:
-                del arg[1]['group']
-                group = extra_groups[group_name]
-            group.add_argument(*arg[0], **arg[1])
+        self._populate_command_group(c_parser, handler, group)
 
         # This will print the description of the command below the usage.
         description = handler.description
         if description:
             parser.description = description
 
         parser.usage = '%(prog)s [global arguments] ' + command + \
             ' [command arguments]'
         parser.print_help()
         print('')
         c_parser.print_help()
 
+    def _handle_subcommand_main_help(self, parser, handler):
+        parser.usage = '%(prog)s [global arguments] ' + handler.name + \
+            ' subcommand [subcommand arguments]'
+        group = parser.add_argument_group('Sub Commands')
+
+        for subcommand, subhandler in sorted(handler.subcommand_handlers.iteritems()):
+            group.add_argument(subcommand, help=subhandler.description,
+                action='store_true')
+
+        parser.print_help()
+
+    def _handle_subcommand_help(self, parser, command, subcommand, handler):
+        parser.usage = '%(prog)s [global arguments] ' + command + \
+            ' ' + subcommand + ' [command arguments]'
+
+        c_parser = argparse.ArgumentParser(add_help=False,
+            formatter_class=CommandFormatter)
+        group = c_parser.add_argument_group('Sub Command Arguments')
+        self._populate_command_group(c_parser, handler, group)
+
+        parser.print_help()
+        print('')
+        c_parser.print_help()
+
 class NoUsageFormatter(argparse.HelpFormatter):
     def _format_usage(self, *args, **kwargs):
         return ""
--- a/python/moz.build
+++ b/python/moz.build
@@ -1,21 +1,24 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 SPHINX_PYTHON_PACKAGE_DIRS += [
     'codegen',
+    'mach',
     'mozbuild/mozbuild',
     'mozbuild/mozpack',
     'mozversioncontrol/mozversioncontrol',
 ]
 
+SPHINX_TREES['mach'] = 'mach/docs'
+
 PYTHON_UNIT_TESTS += [
     'mach/mach/test/__init__.py',
     'mach/mach/test/common.py',
     'mach/mach/test/test_conditions.py',
     'mach/mach/test/test_config.py',
     'mach/mach/test/test_entry_point.py',
     'mach/mach/test/test_error_output.py',
     'mach/mach/test/test_logger.py',
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -996,20 +996,22 @@ class RunProgram(MachCommandBase):
                     "LD_PRELOAD": dmd_lib,
                     "LD_LIBRARY_PATH": bin_dir,
                 },
                 "WINNT": {
                     "MOZ_REPLACE_MALLOC_LIB": dmd_lib,
                 },
             }
 
+            arch = self.substs['OS_ARCH']
+
             if dmd_params:
-                env_vars["DMD"] = " ".join(dmd_params)
+                env_vars[arch]["DMD"] = " ".join(dmd_params)
 
-            extra_env.update(env_vars.get(self.substs['OS_ARCH'], {}))
+            extra_env.update(env_vars.get(arch, {}))
 
         return self.run_process(args=args, ensure_exit_code=False,
             pass_thru=True, append_env=extra_env)
 
 @CommandProvider
 class Buildsymbols(MachCommandBase):
     """Produce a package of debug symbols suitable for use with Breakpad."""
 
--- a/rdf/build/moz.build
+++ b/rdf/build/moz.build
@@ -13,8 +13,10 @@ SOURCES += [
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/rdf/base',
     '/rdf/datasource',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/rdf/tests/rdfcat/moz.build
+++ b/rdf/tests/rdfcat/moz.build
@@ -6,8 +6,10 @@
 
 GeckoProgram('rdfcat', linkage='dependent')
 
 SOURCES += [
     'rdfcat.cpp',
 ]
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
+
+FAIL_ON_WARNINGS = True
--- a/rdf/tests/rdfpoll/moz.build
+++ b/rdf/tests/rdfpoll/moz.build
@@ -4,8 +4,10 @@
 # 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/.
 
 GeckoProgram('rdfpoll', linkage='dependent')
 
 SOURCES += [
     'rdfpoll.cpp',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/rdf/tests/triplescat/moz.build
+++ b/rdf/tests/triplescat/moz.build
@@ -6,8 +6,10 @@
 
 GeckoProgram('triplescat', linkage='dependent')
 
 SOURCES += [
     'triplescat.cpp',
 ]
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
+
+FAIL_ON_WARNINGS = True
--- a/rdf/util/internal/moz.build
+++ b/rdf/util/internal/moz.build
@@ -4,8 +4,10 @@
 # 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('../objs.mozbuild')
 
 SOURCES += rdf_util_src_cppsrcs
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/rdf/util/moz.build
+++ b/rdf/util/moz.build
@@ -13,8 +13,10 @@ EXPORTS += [
 include('objs.mozbuild')
 
 SOURCES += rdf_util_src_cppsrcs
 
 Library('rdfutil_external_s')
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = True
+
+FAIL_ON_WARNINGS = True
--- a/startupcache/moz.build
+++ b/startupcache/moz.build
@@ -21,8 +21,10 @@ EXPORTS.mozilla.scache += [
 # These files cannot be built in unified mode because they rely on plarena.h.
 SOURCES += [
     'StartupCache.cpp',
     'StartupCacheModule.cpp',
     'StartupCacheUtils.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,13 +1,13 @@
 import os
 from setuptools import setup, find_packages
 import sys
 
-version = '0.8.5'
+version = '0.8.6'
 
 # dependencies
 with open('requirements.txt') as f:
     deps = f.read().splitlines()
 
 setup(name='marionette_client',
       version=version,
       description="Marionette test automation client",
--- a/testing/tools/screenshot/moz.build
+++ b/testing/tools/screenshot/moz.build
@@ -18,8 +18,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
         'win32-screenshot.cpp',
     ]
     USE_STATIC_LIBS = True
     if CONFIG['GNU_CC']:
         WIN32_EXE_LDFLAGS += ['-municode']
     OS_LIBS += [
         'gdiplus',
     ]
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/components/ctypes/tests/moz.build
+++ b/toolkit/components/ctypes/tests/moz.build
@@ -1,21 +1,24 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
+
 NO_DIST_INSTALL = True
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
 
 UNIFIED_SOURCES += [
     'jsctypes-test-errno.cpp',
     'jsctypes-test-finalizer.cpp',
     'jsctypes-test.cpp',
 ]
 
 SharedLibrary('jsctypes-test')
 
 LOCAL_INCLUDES += [
     '/js/src/ctypes',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/devtools/server/moz.build
+++ b/toolkit/devtools/server/moz.build
@@ -69,8 +69,9 @@ EXTRA_JS_MODULES.devtools.server.actors 
     'actors/webgl.js',
 ]
 
 EXTRA_JS_MODULES.devtools.server.actors.utils += [
     'actors/utils/make-debugger.js',
     'actors/utils/map-uri-to-addon-id.js'
 ]
 
+FAIL_ON_WARNINGS = True
--- a/toolkit/library/StaticXULComponentsEnd/moz.build
+++ b/toolkit/library/StaticXULComponentsEnd/moz.build
@@ -8,8 +8,10 @@ SOURCES += [
 
 # Don't let LTO reorder StaticXULComponentsStart.o.
 if '-flto' in CONFIG['OS_CXXFLAGS']:
     SOURCES['StaticXULComponentsEnd.cpp'].flags += ['-fno-lto']
 
 Library('StaticXULComponentsEnd')
 
 DEFINES['MOZILLA_INTERNAL_API'] = True
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/system/androidproxy/moz.build
+++ b/toolkit/system/androidproxy/moz.build
@@ -4,8 +4,10 @@
 # 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/.
 
 SOURCES += [
     'nsAndroidSystemProxySettings.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/system/dbus/moz.build
+++ b/toolkit/system/dbus/moz.build
@@ -11,8 +11,10 @@ SOURCES += [
 ]
 
 XPCOMBinaryComponent('dbusservice')
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
 CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
 
 OS_LIBS += CONFIG['MOZ_DBUS_GLIB_LIBS']
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/system/osxproxy/moz.build
+++ b/toolkit/system/osxproxy/moz.build
@@ -4,8 +4,10 @@
 # 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/.
 
 SOURCES += [
     'nsOSXSystemProxySettings.mm',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/system/unixproxy/moz.build
+++ b/toolkit/system/unixproxy/moz.build
@@ -10,8 +10,10 @@ if CONFIG['MOZ_ENABLE_LIBPROXY']:
         'nsLibProxySettings.cpp',
     ]
 else:
     SOURCES += [
         'nsUnixSystemProxySettings.cpp',
     ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/system/windowsproxy/moz.build
+++ b/toolkit/system/windowsproxy/moz.build
@@ -4,8 +4,10 @@
 # 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/.
 
 SOURCES += [
     'nsWindowsSystemProxySettings.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -157,14 +157,18 @@ LOCAL_INCLUDES += [
 
 if CONFIG['MOZ_ENABLE_XREMOTE']:
     LOCAL_INCLUDES += [
         '/widget/xremoteclient',
     ]
 
 if CONFIG['MOZ_B2G_LOADER']:
     DEFINES['OMNIJAR_NAME'] = CONFIG['OMNIJAR_NAME']
+
 CXXFLAGS += CONFIG['TK_CFLAGS']
 CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
 CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
 
 if CONFIG['MOZ_WIDGET_GTK']:
     CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
+
+if CONFIG['OS_TARGET'] != 'Darwin':
+    FAIL_ON_WARNINGS = True
--- a/webapprt/mac/moz.build
+++ b/webapprt/mac/moz.build
@@ -22,8 +22,10 @@ LOCAL_INCLUDES += [
     '/xpcom/build',
 ]
 
 USE_LIBS += [
     'xpcomglue',
 ]
 
 DISABLE_STL_WRAPPING = True
+
+FAIL_ON_WARNINGS = True
--- a/webapprt/win/moz.build
+++ b/webapprt/win/moz.build
@@ -49,8 +49,10 @@ USE_LIBS += [
     'xpcomglue_staticruntime',
 ]
 
 DISABLE_STL_WRAPPING = True
 
 OS_LIBS += [
     'shell32',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/xpcom/reflect/xptcall/md/unix/moz.build
+++ b/xpcom/reflect/xptcall/md/unix/moz.build
@@ -324,8 +324,10 @@ if CONFIG['OS_ARCH'] == 'Linux':
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../..',
     '/xpcom/reflect/xptinfo',
 ]
 
 NO_PGO = True
+
+FAIL_ON_WARNINGS = True
--- a/xpcom/reflect/xptcall/md/win32/moz.build
+++ b/xpcom/reflect/xptcall/md/win32/moz.build
@@ -36,8 +36,10 @@ else:
         SOURCES['xptcinvoke.cpp'].no_pgo = True
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../..',
     '/xpcom/reflect/xptinfo',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/xpcom/reflect/xptcall/moz.build
+++ b/xpcom/reflect/xptcall/moz.build
@@ -18,8 +18,10 @@ EXPORTS += [
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '/xpcom/reflect/xptinfo',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/xpcom/tests/component/moz.build
+++ b/xpcom/tests/component/moz.build
@@ -16,8 +16,10 @@ DEFINES['LIBRARY_FILENAME'] = '%s%s%s' %
     CONFIG['DLL_PREFIX'],
     LIBRARY_NAME,
     CONFIG['DLL_SUFFIX']
 )
 
 # Need to link with CoreFoundation on Mac
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     OS_LIBS += CONFIG['TK_LIBS']
+
+FAIL_ON_WARNINGS = True
--- a/xpcom/tests/component_no_aslr/moz.build
+++ b/xpcom/tests/component_no_aslr/moz.build
@@ -16,8 +16,10 @@ DEFINES['LIBRARY_FILENAME'] = '%s%s%s' %
     CONFIG['DLL_PREFIX'],
     LIBRARY_NAME,
     CONFIG['DLL_SUFFIX']
 )
 
 # Need to link with CoreFoundation on Mac
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     OS_LIBS += CONFIG['TK_LIBS']
+
+FAIL_ON_WARNINGS = True
--- a/xpfe/components/build/moz.build
+++ b/xpfe/components/build/moz.build
@@ -8,8 +8,10 @@ SOURCES += [
     'nsModule.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../directory',
 ]
+
+FAIL_ON_WARNINGS = True
--- a/xpfe/components/windowds/moz.build
+++ b/xpfe/components/windowds/moz.build
@@ -10,8 +10,10 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'windowds'
 
 SOURCES += [
     'nsWindowDataSource.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/xulrunner/app/moz.build
+++ b/xulrunner/app/moz.build
@@ -48,8 +48,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'shell32',
         'ole32',
         'oleaut32',
         'version',
         'winspool',
     ]
 
 DISABLE_STL_WRAPPING = True
+
+FAIL_ON_WARNINGS = True
--- a/xulrunner/examples/simple/components/src/moz.build
+++ b/xulrunner/examples/simple/components/src/moz.build
@@ -11,8 +11,10 @@ SOURCES += [
 XPCOMBinaryComponent('simpletest')
 
 EXTRA_COMPONENTS += [
     'SimpleTest.js',
     'SimpleTest.manifest',
 ]
 
 XPI_NAME = 'simple'
+
+FAIL_ON_WARNINGS = True
--- a/xulrunner/stub/moz.build
+++ b/xulrunner/stub/moz.build
@@ -42,8 +42,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'shell32',
     ]
 
 DISABLE_STL_WRAPPING = True
 
 # Need to link with CoreFoundation on Mac
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     OS_LIBS += CONFIG['TK_LIBS']
+
+FAIL_ON_WARNINGS = True
--- a/xulrunner/tools/redit/moz.build
+++ b/xulrunner/tools/redit/moz.build
@@ -8,8 +8,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     GeckoProgram('redit')
     SOURCES += [
         'redit.cpp',
     ]
     for var in ('WIN32_LEAN_AND_MEAN', 'UNICODE', '_UNICODE'):
         DEFINES[var] = True
     if CONFIG['GNU_CC']:
         WIN32_EXE_LDFLAGS += ['-municode']
+
+FAIL_ON_WARNINGS = True