merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 11 Apr 2016 11:46:21 +0200
changeset 330429 e847cfcb315f511f4928b03fd47dcf57aad05e1e
parent 330410 1709e72eedc79a1953aa8c2712f6c9c604542172 (current diff)
parent 330428 3baed5c339e31188949641cb7bc7178a71070718 (diff)
child 330434 02a6023f95ceb20beb3f996608c26cf8f5f5753a
child 330467 f50bbe259eb0971d77f68d77fb93dd6d5b49ac1a
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
e847cfcb315f / 48.0a1 / 20160411030231 / files
nightly linux64
e847cfcb315f / 48.0a1 / 20160411030231 / files
nightly mac
e847cfcb315f / 48.0a1 / 20160411030231 / files
nightly win32
e847cfcb315f / 48.0a1 / 20160411030231 / files
nightly win64
e847cfcb315f / 48.0a1 / 20160411030231 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
-skip-if = e10s && debug # bug 1252283
 support-files =
   doc_body_animation.html
   doc_end_delay.html
   doc_frame_script.js
   doc_keyframes.html
   doc_modify_playbackRate.html
   doc_negative_animation.html
   doc_pseudo_elements.html
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -149,16 +149,17 @@ skip-if = e10s # Bug 1067145 - e10s resp
 [browser_telemetry_toolboxtabs_options.js]
 [browser_telemetry_toolboxtabs_shadereditor.js]
 [browser_telemetry_toolboxtabs_storage.js]
 [browser_telemetry_toolboxtabs_styleeditor.js]
 [browser_telemetry_toolboxtabs_webaudioeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
 [browser_templater_basic.js]
 [browser_toolbar_basic.js]
+skip-if = (e10s && debug) # Bug 1253035
 [browser_toolbar_tooltip.js]
 [browser_toolbar_webconsole_errors_count.js]
 skip-if = e10s # The developertoolbar error count isn't correct with e10s
 [browser_treeWidget_basic.js]
 [browser_treeWidget_keyboard_interaction.js]
 [browser_treeWidget_mouse_interaction.js]
 [browser_devices.js]
 [browser_theme_switching.js]
--- a/dom/animation/AnimValuesStyleRule.h
+++ b/dom/animation/AnimValuesStyleRule.h
@@ -31,32 +31,31 @@ public:
 
   // nsIStyleRule implementation
   void MapRuleInfoInto(nsRuleData* aRuleData) override;
   bool MightMapInheritedStyleData() override;
 #ifdef DEBUG
   void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
-  void AddValue(nsCSSProperty aProperty, StyleAnimationValue &aStartValue)
+  void AddValue(nsCSSProperty aProperty, const StyleAnimationValue &aStartValue)
   {
     PropertyValuePair v = { aProperty, aStartValue };
     mPropertyValuePairs.AppendElement(v);
     mStyleBits |=
       nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]);
   }
 
-  // Caller must fill in returned value.
-  StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty)
+  void AddValue(nsCSSProperty aProperty, StyleAnimationValue&& aStartValue)
   {
     PropertyValuePair *p = mPropertyValuePairs.AppendElement();
     p->mProperty = aProperty;
+    p->mValue = Move(aStartValue);
     mStyleBits |=
       nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]);
-    return &p->mValue;
   }
 
   struct PropertyValuePair {
     nsCSSProperty mProperty;
     StyleAnimationValue mValue;
   };
 
 private:
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -593,44 +593,45 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
                  prop.mSegments.Length(),
                "out of array bounds");
 
     if (!aStyleRule) {
       // Allocate the style rule now that we know we have animation data.
       aStyleRule = new AnimValuesStyleRule();
     }
 
-    StyleAnimationValue* val = aStyleRule->AddEmptyValue(prop.mProperty);
-
     // Special handling for zero-length segments
     if (segment->mToKey == segment->mFromKey) {
       if (computedTiming.mProgress.Value() < 0) {
-        *val = segment->mFromValue;
+        aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
       } else {
-        *val = segment->mToValue;
+        aStyleRule->AddValue(prop.mProperty, segment->mToValue);
       }
       continue;
     }
 
     double positionInSegment =
       (computedTiming.mProgress.Value() - segment->mFromKey) /
       (segment->mToKey - segment->mFromKey);
     double valuePosition =
       ComputedTimingFunction::GetPortion(segment->mTimingFunction,
                                          positionInSegment,
                                          computedTiming.mBeforeFlag);
 
-#ifdef DEBUG
-    bool result =
-#endif
-      StyleAnimationValue::Interpolate(prop.mProperty,
-                                       segment->mFromValue,
-                                       segment->mToValue,
-                                       valuePosition, *val);
-    MOZ_ASSERT(result, "interpolate must succeed now");
+    StyleAnimationValue val;
+    if (StyleAnimationValue::Interpolate(prop.mProperty,
+                                         segment->mFromValue,
+                                         segment->mToValue,
+                                         valuePosition, val)) {
+      aStyleRule->AddValue(prop.mProperty, Move(val));
+    } else if (valuePosition < 0.5) {
+      aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
+    } else {
+      aStyleRule->AddValue(prop.mProperty, segment->mToValue);
+    }
   }
 }
 
 bool
 KeyframeEffectReadOnly::IsRunningOnCompositor() const
 {
   // We consider animation is running on compositor if there is at least
   // one property running on compositor.
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1252677 for Windows, Bug 1252348 for Mac
+skip-if = (e10s && debug && os == 'win') # Bug 1252677
 support-files =
   android.json
   file_drawImage_document_domain.html
   image_anim-gr.gif
   image_anim-gr.png
   image_anim-poster-gr.png
   image_broken.png
   image_error-early.png
--- a/dom/canvas/test/reftest/filters/global-alpha.html
+++ b/dom/canvas/test/reftest/filters/global-alpha.html
@@ -2,16 +2,16 @@
 <html>
 <body>
 <canvas id="canvas" width="100" height="100"></canvas>
 <script>
 
 var canvas = document.getElementById('canvas');
 var ctx = canvas.getContext('2d');
 
-ctx.filter = 'drop-shadow(0 10px black)';
+ctx.filter = 'drop-shadow(0 10px #000)';
 ctx.globalAlpha = 0.5;
 ctx.fillStyle = '#0f0';
 ctx.fillRect(25, 25, 50, 40);
 
 </script>
 </body>
 </html>
--- a/dom/canvas/test/reftest/filters/reftest.list
+++ b/dom/canvas/test/reftest/filters/reftest.list
@@ -1,14 +1,14 @@
 default-preferences pref(canvas.filters.enabled,true)
 
 == default-color.html ref.html
 == drop-shadow.html ref.html
 == drop-shadow-transformed.html ref.html
-== global-alpha.html global-alpha-ref.html
+fuzzy-if(azureSkia,1,1500) == global-alpha.html global-alpha-ref.html
 == global-composite-operation.html global-composite-operation-ref.html
 == liveness.html ref.html
 == multiple-drop-shadows.html shadow-ref.html
 == shadow.html shadow-ref.html
 == subregion-fill-paint.html subregion-ref.html
 == subregion-stroke-paint.html subregion-ref.html
 == svg-bbox.html svg-bbox-ref.html
 == svg-inline.html ref.html
--- a/dom/canvas/test/reftest/filters/units-em.html
+++ b/dom/canvas/test/reftest/filters/units-em.html
@@ -5,13 +5,17 @@
 <script>
 
 var canvas = document.getElementById('canvas');
 var ctx = canvas.getContext('2d');
 
 ctx.font = '20px sans-serif';
 ctx.filter = 'drop-shadow(0 .5em black)';
 ctx.fillStyle = '#0f0';
-ctx.fillRect(25, 25, 25, 40);
+ctx.fillRect(25, 25, 50, 40);
 
 canvas.style.fontSize = '5px';
 ctx.font = '4em sans-serif';
 ctx.filter = 'drop-shadow(0 .5em black)';
+
+</script>
+</body>
+</html>
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -154,8 +154,11 @@ skip-if(!winWidget) pref(webgl.disable-a
 # focus rings
 pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
 pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(Android) skip-if(winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
 
 # Check that captureStream() displays in a local video element
 == capturestream.html wrapper.html?green.png
 
 fuzzy-if(azureSkiaGL,1,2) fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
+
+# Canvas Filter Reftests
+include filters/reftest.list
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4107,24 +4107,42 @@ void HTMLMediaElement::ChangeNetworkStat
 
 bool HTMLMediaElement::CanActivateAutoplay()
 {
   // For stream inputs, we activate autoplay on HAVE_NOTHING because
   // this element itself might be blocking the stream from making progress by
   // being paused. We also activate autopaly when playing a media source since
   // the data download is controlled by the script and there is no way to
   // evaluate MediaDecoder::CanPlayThrough().
-  return !mPausedForInactiveDocumentOrChannel &&
-         mAutoplaying &&
-         mPaused &&
-         ((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
-          mSrcStream || mMediaSource) &&
-         HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
-         mAutoplayEnabled &&
-         !IsEditable();
+
+  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) || !mAutoplayEnabled) {
+    return false;
+  }
+
+  if (!mAutoplaying) {
+    return false;
+  }
+
+  if (IsEditable()) {
+    return false;
+  }
+
+  if (!mPaused) {
+    return false;
+  }
+
+  if (mPausedForInactiveDocumentOrChannel) {
+    return false;
+  }
+
+  bool hasData =
+    (mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
+    mSrcStream || mMediaSource;
+
+  return hasData;
 }
 
 void HTMLMediaElement::CheckAutoplayDataReady()
 {
   if (!CanActivateAutoplay()) {
     return;
   }
 
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -809,16 +809,17 @@ bool WebMDemuxer::GetOffsetForTime(uint6
 
 
 //WebMTrackDemuxer
 WebMTrackDemuxer::WebMTrackDemuxer(WebMDemuxer* aParent,
                                    TrackInfo::TrackType aType,
                                    uint32_t aTrackNumber)
   : mParent(aParent)
   , mType(aType)
+  , mNeedKeyframe(true)
 {
   mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
   MOZ_ASSERT(mInfo);
 }
 
 WebMTrackDemuxer::~WebMTrackDemuxer()
 {
   mSamples.Reset();
@@ -835,16 +836,17 @@ WebMTrackDemuxer::Seek(media::TimeUnit a
 {
   // Seeks to aTime. Upon success, SeekPromise will be resolved with the
   // actual time seeked to. Typically the random access point time
 
   media::TimeUnit seekTime = aTime;
   mSamples.Reset();
   mParent->SeekInternal(aTime);
   mParent->GetNextPacket(mType, &mSamples);
+  mNeedKeyframe = true;
 
   // Check what time we actually seeked to.
   if (mSamples.GetSize() > 0) {
     const RefPtr<MediaRawData>& sample = mSamples.First();
     seekTime = media::TimeUnit::FromMicroseconds(sample->mTime);
   }
   SetNextKeyFrameTime();
 
@@ -870,16 +872,20 @@ WebMTrackDemuxer::GetSamples(int32_t aNu
     return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   while (aNumSamples) {
     RefPtr<MediaRawData> sample(NextSample());
     if (!sample) {
       break;
     }
+    if (mNeedKeyframe && !sample->mKeyframe) {
+      continue;
+    }
+    mNeedKeyframe = false;
     samples->mSamples.AppendElement(sample);
     aNumSamples--;
   }
 
   if (samples->mSamples.IsEmpty()) {
     return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__);
   } else {
     UpdateSamples(samples->mSamples);
@@ -946,16 +952,17 @@ WebMTrackDemuxer::SetNextKeyFrameTime()
   }
 }
 
 void
 WebMTrackDemuxer::Reset()
 {
   mSamples.Reset();
   media::TimeIntervals buffered = GetBuffered();
+  mNeedKeyframe = true;
   if (buffered.Length()) {
     WEBM_DEBUG("Seek to start point: %f", buffered.Start(0).ToSeconds());
     mParent->SeekInternal(buffered.Start(0));
     SetNextKeyFrameTime();
   } else {
     mNextKeyframeTime.reset();
   }
 }
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -237,16 +237,17 @@ private:
   ~WebMTrackDemuxer();
   void UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples);
   void SetNextKeyFrameTime();
   RefPtr<MediaRawData> NextSample ();
   RefPtr<WebMDemuxer> mParent;
   TrackInfo::TrackType mType;
   UniquePtr<TrackInfo> mInfo;
   Maybe<media::TimeUnit> mNextKeyframeTime;
+  bool mNeedKeyframe;
 
   // Queued samples extracted by the demuxer, but not yet returned.
   MediaRawDataQueue mSamples;
 };
 
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1926,17 +1926,17 @@ Layer::PrintInfo(std::stringstream& aStr
     aStream << " [not visible]";
   }
   if (!mEventRegions.IsEmpty()) {
     AppendToString(aStream, mEventRegions, " ", "");
   }
   if (1.0 != mOpacity) {
     aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get();
   }
-  if (GetContentFlags() & CONTENT_OPAQUE) {
+  if (IsOpaque()) {
     aStream << " [opaqueContent]";
   }
   if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
     aStream << " [componentAlpha]";
   }
   if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
     aStream << " [backfaceHidden]";
   }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1497,16 +1497,22 @@ public:
   bool IsVisible() {
     // For containers extending 3D context, visible region
     // is meaningless, since they are just intermediate result of
     // content.
     return !GetLocalVisibleRegion().IsEmpty() || Extend3DContext();
   }
 
   /**
+   * Return true if current layer content is opaque.
+   * It does not guarantee that layer content is always opaque.
+   */
+  virtual bool IsOpaque() { return GetContentFlags() & CONTENT_OPAQUE; }
+
+  /**
    * Returns the product of the opacities of this layer and all ancestors up
    * to and excluding the nearest ancestor that has UseIntermediateSurface() set.
    */
   float GetEffectiveOpacity();
 
   /**
    * Returns the blendmode of this layer.
    */
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -576,16 +576,37 @@ ImageHost::GetImageSize() const
 
   const TimedImage* img = ChooseImage();
   if (img) {
     return IntSize(img->mPictureRect.width, img->mPictureRect.height);
   }
   return IntSize();
 }
 
+bool
+ImageHost::IsOpaque()
+{
+  const TimedImage* img = ChooseImage();
+  if (!img) {
+    return false;
+  }
+
+  if (img->mPictureRect.width == 0 ||
+      img->mPictureRect.height == 0 ||
+      !img->mTextureHost) {
+    return false;
+  }
+
+  gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
+  if (gfx::IsOpaque(format)) {
+    return true;
+  }
+  return false;
+}
+
 already_AddRefed<TexturedEffect>
 ImageHost::GenEffect(const gfx::Filter& aFilter)
 {
   TimedImage* img = ChooseImage();
   if (!img) {
     return nullptr;
   }
   SetCurrentTextureHost(img->mTextureHost);
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -110,16 +110,18 @@ public:
     // Don't apply bias to frame times
     BIAS_NONE,
     // Apply a negative bias to frame times to keep them before the vsync time
     BIAS_NEGATIVE,
     // Apply a positive bias to frame times to keep them after the vsync time
     BIAS_POSITIVE,
   };
 
+  bool IsOpaque();
+
 protected:
   struct TimedImage {
     RefPtr<TextureHost> mTextureHost;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
     int32_t mFrameID;
     int32_t mProducerID;
     int32_t mInputFrameID;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -10,16 +10,17 @@
 #include "gfxEnv.h"                     // for gfxEnv
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for EffectChain
+#include "mozilla/layers/ImageHost.h"   // for ImageHost
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsAString.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsString.h"                   // for nsAutoCString
 
@@ -45,17 +46,17 @@ ImageLayerComposite::~ImageLayerComposit
   CleanupResources();
 }
 
 bool
 ImageLayerComposite::SetCompositableHost(CompositableHost* aHost)
 {
   switch (aHost->GetType()) {
     case CompositableType::IMAGE:
-      mImageHost = aHost;
+      mImageHost = static_cast<ImageHost*>(aHost);
       return true;
     default:
       return false;
   }
 }
 
 void
 ImageLayerComposite::Disconnect()
@@ -74,16 +75,26 @@ ImageLayerComposite::GetRenderState()
 
 Layer*
 ImageLayerComposite::GetLayer()
 {
   return this;
 }
 
 void
+ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+  LayerComposite::SetLayerManager(aManager);
+  mManager = aManager;
+  if (mImageHost) {
+    mImageHost->SetCompositor(mCompositor);
+  }
+}
+
+void
 ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
 {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return;
   }
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxEnv::DumpCompositorTextures()) {
@@ -138,16 +149,30 @@ ImageLayerComposite::ComputeEffectiveTra
         SnapTransformTranslation(aTransformToSurface, nullptr);
   } else {
     mEffectiveTransformForBuffer = mEffectiveTransform;
   }
 
   ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
+bool
+ImageLayerComposite::IsOpaque()
+{
+  if (!mImageHost ||
+      !mImageHost->IsAttached()) {
+    return false;
+  }
+
+  if (mScaleMode == ScaleMode::STRETCH) {
+    return mImageHost->IsOpaque();
+  }
+  return false;
+}
+
 CompositableHost*
 ImageLayerComposite::GetCompositableHost()
 {
   if (mImageHost && mImageHost->IsAttached()) {
     return mImageHost.get();
   }
 
   return nullptr;
--- a/gfx/layers/composite/ImageLayerComposite.h
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -38,45 +38,40 @@ public:
   virtual LayerRenderState GetRenderState() override;
 
   virtual void Disconnect() override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual Layer* GetLayer() override;
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override
-  {
-    LayerComposite::SetLayerManager(aManager);
-    mManager = aManager;
-    if (mImageHost) {
-      mImageHost->SetCompositor(mCompositor);
-    }
-  }
+  virtual void SetLayerManager(LayerManagerComposite* aManager) override;
 
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
 
   virtual void CleanupResources() override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   virtual LayerComposite* AsLayerComposite() override { return this; }
 
   virtual const char* Name() const override { return "ImageLayerComposite"; }
 
+  virtual bool IsOpaque() override;
+
 protected:
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
 private:
   gfx::Filter GetEffectFilter();
 
 private:
-  RefPtr<CompositableHost> mImageHost;
+  RefPtr<ImageHost> mImageHost;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_ImageLayerComposite_H */
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -357,17 +357,17 @@ LayerManagerComposite::PostProcessLayers
   aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
       PixelCastJustification::MovingDownToChildren));
 
   // If we have a simple transform, then we can add our opaque area into
   // aOpaqueRegion.
   if (integerTranslation &&
       !aLayer->HasMaskLayers() &&
       aLayer->IsOpaqueForVisibility()) {
-    if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
+    if (aLayer->IsOpaque()) {
       localOpaque.OrWith(composite->GetFullyRenderedRegion());
     }
     localOpaque.MoveBy(*integerTranslation);
     if (layerClip) {
       localOpaque.AndWith(layerClip->ToUnknownRect());
     }
     aOpaqueRegion.OrWith(localOpaque);
   }
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -526,17 +526,16 @@ SurfaceTextureHost::SetCompositor(Compos
   if (mTextureSource) {
     mTextureSource->SetCompositor(glCompositor);
   }
 }
 
 gfx::SurfaceFormat
 SurfaceTextureHost::GetFormat() const
 {
-  MOZ_ASSERT(mTextureSource);
   return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
 }
 
 void
 SurfaceTextureHost::DeallocateDeviceData()
 {
   if (mTextureSource) {
     mTextureSource->DeallocateDeviceData();
--- a/image/test/mochitest/chrome.ini
+++ b/image/test/mochitest/chrome.ini
@@ -1,18 +1,16 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   animationPolling.js
+  animated-gif.gif
   iframe.html
   imgutils.js
   ref-iframe.html
 
 [test_animation.html]
-disabled = bug 1100497
 [test_animation2.html]
-disabled = bug 1101415
 [test_bug415761.html]
 skip-if = os != "win" || os_version == "6.2"
 support-files =
   bug415761.ico
 [test_undisplayed_iframe.html]
-disabled = bug 1060869
--- a/image/test/reftest/gif/reftest.list
+++ b/image/test/reftest/gif/reftest.list
@@ -45,9 +45,9 @@ skip == test_bug641198.html animation2a-
 # \r\n
 # <contents of blue.gif> (no newline)
 # --BOUNDARYOMG--\r\n
 # 
 # (The boundary is arbitrary, and just has to be defined as something that
 # won't be in the text of the contents themselves. --$(boundary)\r\n means
 # "Here is the beginning of a boundary," and --$(boundary)-- means "All done
 # sending you parts.")
-skip-if(B2G||browserIsRemote) HTTP == webcam.html blue.gif # bug 773482
+skip-if(B2G) HTTP == webcam.html blue.gif # bug 773482
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -368,16 +368,22 @@ public:
 
   explicit StyleAnimationValue(Unit aUnit = eUnit_Null) : mUnit(aUnit) {
     NS_ASSERTION(aUnit == eUnit_Null || aUnit == eUnit_Normal ||
                  aUnit == eUnit_Auto || aUnit == eUnit_None,
                  "must be valueless unit");
   }
   StyleAnimationValue(const StyleAnimationValue& aOther)
     : mUnit(eUnit_Null) { *this = aOther; }
+  StyleAnimationValue(StyleAnimationValue&& aOther)
+    : mUnit(aOther.mUnit)
+    , mValue(aOther.mValue)
+  {
+    aOther.mUnit = eUnit_Null;
+  }
   enum IntegerConstructorType { IntegerConstructor };
   StyleAnimationValue(int32_t aInt, Unit aUnit, IntegerConstructorType);
   enum CoordConstructorType { CoordConstructor };
   StyleAnimationValue(nscoord aLength, CoordConstructorType);
   enum PercentConstructorType { PercentConstructor };
   StyleAnimationValue(float aPercent, PercentConstructorType);
   enum FloatConstructorType { FloatConstructor };
   StyleAnimationValue(float aFloat, FloatConstructorType);
--- a/testing/mozharness/mozharness/mozilla/taskcluster_helper.py
+++ b/testing/mozharness/mozharness/mozilla/taskcluster_helper.py
@@ -95,16 +95,19 @@ class Taskcluster(LogMixin):
     def expiration(self):
         return datetime.utcnow() + timedelta(weeks=52)
 
     def create_artifact(self, task, filename):
         mime_type = self.get_mime_type(os.path.splitext(filename)[1])
         content_length = os.path.getsize(filename)
         self.info("Uploading to S3: filename=%s mimetype=%s length=%s" % (
             filename, mime_type, content_length))
+        # reclaim the task to avoid "claim-expired" errors
+        self.taskcluster_queue.reclaimTask(
+            task['status']['taskId'], task['status']['runs'][-1]['runId'])
         artifact = self.taskcluster_queue.createArtifact(
             task['status']['taskId'],
             task['status']['runs'][-1]['runId'],
             'public/build/%s' % os.path.basename(filename),
             {
                 "storageType": "s3",
                 "expires": self.expiration,
                 "contentType": mime_type,
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -28385,20 +28385,16 @@
         "path": "vibration/silent-ignore.html",
         "url": "/vibration/silent-ignore.html"
       },
       {
         "path": "web-animations/animatable/animate.html",
         "url": "/web-animations/animatable/animate.html"
       },
       {
-        "path": "web-animations/animation-effect-timing/delay.html",
-        "url": "/web-animations/animation-effect-timing/delay.html"
-      },
-      {
         "path": "web-animations/animation-effect-timing/direction.html",
         "url": "/web-animations/animation-effect-timing/direction.html"
       },
       {
         "path": "web-animations/animation-effect-timing/duration.html",
         "url": "/web-animations/animation-effect-timing/duration.html"
       },
       {
@@ -28417,16 +28413,20 @@
         "path": "web-animations/animation-effect-timing/iterationStart.html",
         "url": "/web-animations/animation-effect-timing/iterationStart.html"
       },
       {
         "path": "web-animations/animation-effect-timing/iterations.html",
         "url": "/web-animations/animation-effect-timing/iterations.html"
       },
       {
+        "path": "web-animations/animation-model/animation-types/discrete-animation.html",
+        "url": "/web-animations/animation-model/animation-types/discrete-animation.html"
+      },
+      {
         "path": "web-animations/animation-timeline/document-timeline.html",
         "url": "/web-animations/animation-timeline/document-timeline.html"
       },
       {
         "path": "web-animations/animation-timeline/idlharness.html",
         "url": "/web-animations/animation-timeline/idlharness.html"
       },
       {
--- a/testing/web-platform/meta/XMLHttpRequest/event-progress.htm.ini
+++ b/testing/web-platform/meta/XMLHttpRequest/event-progress.htm.ini
@@ -1,15 +1,7 @@
 [event-progress.htm]
   type: testharness
   expected:
-    if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): OK
-    if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): OK
-    if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): OK
-    if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): OK
-    if debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86_64") and (bits == 64): OK
-    if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): OK
-    if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): OK
-    if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): OK
-    if debug and (os == "win") and (version == "10.0.10240") and (processor == "x86_64") and (bits == 64): OK
-    if debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): OK
-    if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): OK
+    if (asan or debug) and (os == "linux"): OK
+    if debug and (os == "mac"): OK
+    if debug and (os == "win"): OK
     TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for discrete animation</title>
+<link rel="help" href="http://w3c.github.io/web-animations/#animatable-as-string-section">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<link rel="stylesheet" href="/resources/testharness.css">
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+  var div = createDiv(t);
+
+  var anim = div.animate({ fontStyle: [ 'normal', 'italic' ] },
+                         { duration: 1000, fill: 'forwards' });
+
+  assert_equals(getComputedStyle(div).fontStyle, 'normal',
+                'Animation produces \'from\' value at start of interval');
+  anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
+  assert_equals(getComputedStyle(div).fontStyle, 'normal',
+                'Animation produces \'from\' value just before the middle of'
+                + ' the interval');
+  anim.currentTime++;
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'to\' value at exact middle of'
+                + ' the interval');
+  anim.finish();
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'to\' value during forwards fill');
+}, 'Test animating discrete values');
+
+test(function(t) {
+  var div = createDiv(t);
+  var originalHeight = getComputedStyle(div).height;
+
+  var anim = div.animate({ height: [ 'auto', '200px' ] },
+                         { duration: 1000, fill: 'forwards' });
+
+  assert_equals(getComputedStyle(div).height, originalHeight,
+                'Animation produces \'from\' value at start of interval');
+  anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
+  assert_equals(getComputedStyle(div).height, originalHeight,
+                'Animation produces \'from\' value just before the middle of'
+                + ' the interval');
+  anim.currentTime++;
+  assert_equals(getComputedStyle(div).height, '200px',
+                'Animation produces \'to\' value at exact middle of'
+                + ' the interval');
+  anim.finish();
+  assert_equals(getComputedStyle(div).height, '200px',
+                'Animation produces \'to\' value during forwards fill');
+}, 'Test discrete animation is used when interpolation fails');
+
+test(function(t) {
+  var div = createDiv(t);
+  var originalHeight = getComputedStyle(div).height;
+
+  var anim = div.animate({ height: [ 'auto',
+                                     '200px',
+                                     '300px',
+                                     'auto',
+                                     '400px' ] },
+                         { duration: 1000, fill: 'forwards' });
+
+  // There are five values, so there are four pairs to try to interpolate.
+  // We test at the middle of each pair.
+  assert_equals(getComputedStyle(div).height, originalHeight,
+                'Animation produces \'from\' value at start of interval');
+  anim.currentTime = 125;
+  assert_equals(getComputedStyle(div).height, '200px',
+                'First non-interpolable pair uses discrete interpolation');
+  anim.currentTime += 250;
+  assert_equals(getComputedStyle(div).height, '250px',
+                'Second interpolable pair uses linear interpolation');
+  anim.currentTime += 250;
+  assert_equals(getComputedStyle(div).height, originalHeight,
+                'Third non-interpolable pair uses discrete interpolation');
+  anim.currentTime += 250;
+  assert_equals(getComputedStyle(div).height, '400px',
+                'Fourth non-interpolable pair uses discrete interpolation');
+}, 'Test discrete animation is used only for pairs of values that cannot'
+   + ' be interpolated');
+
+test(function(t) {
+  var div = createDiv(t);
+  var originalHeight = getComputedStyle(div).height;
+
+  // Easing: http://cubic-bezier.com/#.68,0,1,.01
+  // With this curve, we don't reach the 50% point until about 95% of
+  // the time has expired.
+  var anim = div.animate({ fontStyle: [ 'italic', 'oblique' ] },
+                         { duration: 1000, fill: 'forwards',
+                           easing: 'cubic-bezier(0.68,0,1,0.01)' });
+
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'from\' value at start of interval');
+  anim.currentTime = 940;
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'from\' value at 94% of the iteration'
+                + ' time');
+  anim.currentTime = 960;
+  assert_equals(getComputedStyle(div).fontStyle, 'oblique',
+                'Animation produces \'to\' value at 96% of the iteration'
+                + ' time');
+}, 'Test the 50% switch point for discrete animation is based on the'
+   + ' effect easing');
+
+test(function(t) {
+  var div = createDiv(t);
+  var originalHeight = getComputedStyle(div).height;
+
+  // Easing: http://cubic-bezier.com/#.68,0,1,.01
+  // With this curve, we don't reach the 50% point until about 95% of
+  // the time has expired.
+  var anim = div.animate([ { fontStyle: 'italic',
+                             easing: 'cubic-bezier(0.68,0,1,0.01)' },
+                           { fontStyle: 'oblique' } ],
+                         { duration: 1000, fill: 'forwards' });
+
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'from\' value at start of interval');
+  anim.currentTime = 940;
+  assert_equals(getComputedStyle(div).fontStyle, 'italic',
+                'Animation produces \'from\' value at 94% of the iteration'
+                + ' time');
+  anim.currentTime = 960;
+  assert_equals(getComputedStyle(div).fontStyle, 'oblique',
+                'Animation produces \'to\' value at 96% of the iteration'
+                + ' time');
+}, 'Test the 50% switch point for discrete animation is based on the'
+   + ' keyframe easing');
+
+</script>