merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 26 Mar 2017 20:49:42 +0200
changeset 397803 cc53710589fb500610495da5258b7b9221edf681
parent 397799 1c0b16d1747c0964577a8ec21fa2b0a17b44ea4e (current diff)
parent 397802 f2bc0c02b50c5417f6a7f10345e22326df28fee4 (diff)
child 397806 be5bc1b5a566ff1ab434dfce1d40666061339973
child 397864 6f31760f0ffae62ca715a2b74f017ac059160bda
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.0a1
first release with
nightly mac
cc53710589fb / 55.0a1 / 20170327030203 / files
nightly win32
cc53710589fb / 55.0a1 / 20170327030203 / files
nightly win64
cc53710589fb / 55.0a1 / 20170327030203 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: IYekgpFCtwg
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -82,17 +82,21 @@ LogToConsole(const nsAString& aMsg)
 ////////////////////////////////////////////////////////////////////////////////
 
 namespace {
 
 nsresult
 GetOriginFromPrincipal(nsIPrincipal* aPrincipal, nsACString& aOrigin)
 {
   nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // The principal may belong to the about:blank content viewer, so this can be
+  // expected to fail.
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   nsAutoCString suffix;
   rv = aPrincipal->GetOriginSuffix(suffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozilla::OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(suffix)) {
     return NS_ERROR_FAILURE;
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -21,46 +21,90 @@ using namespace gfx;
 
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnimationState implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 void
-AnimationState::NotifyDecodeComplete()
+AnimationState::UpdateState(bool aAnimationFinished,
+                            RasterImage *aImage,
+                            const gfx::IntSize& aSize)
+{
+  LookupResult result =
+    SurfaceCache::Lookup(ImageKey(aImage),
+                         RasterSurfaceKey(aSize,
+                                          DefaultSurfaceFlags(),
+                                          PlaybackType::eAnimated));
+
+  UpdateStateInternal(result, aAnimationFinished);
+}
+
+void
+AnimationState::UpdateStateInternal(LookupResult& aResult,
+                                    bool aAnimationFinished)
 {
-  // If we weren't discarded before the decode finished then mark ourselves as
-  // currently decoded.
-  if (!mDiscarded) {
-    mIsCurrentlyDecoded = true;
+  // Update mDiscarded and mIsCurrentlyDecoded.
+  if (aResult.Type() == MatchType::NOT_FOUND) {
+    // no frames, we've either been discarded, or never been decoded before.
+    mDiscarded = mHasBeenDecoded;
+    mIsCurrentlyDecoded = false;
+  } else if (aResult.Type() == MatchType::PENDING) {
+    // no frames yet, but a decoder is or will be working on it.
+    mDiscarded = false;
+    mIsCurrentlyDecoded = false;
+  } else {
+    MOZ_ASSERT(aResult.Type() == MatchType::EXACT);
+    mDiscarded = false;
 
+    // If mHasBeenDecoded is true then we know the true total frame count and
+    // we can use it to determine if we have all the frames now so we know if
+    // we are currently fully decoded.
+    // If mHasBeenDecoded is false then we'll get another UpdateState call
+    // when the decode finishes.
+    if (mHasBeenDecoded) {
+      Maybe<uint32_t> frameCount = FrameCount();
+      MOZ_ASSERT(frameCount.isSome());
+      aResult.Surface().Seek(*frameCount - 1);
+      if (aResult.Surface() && aResult.Surface()->IsFinished()) {
+        mIsCurrentlyDecoded = true;
+      } else {
+        mIsCurrentlyDecoded = false;
+      }
+    }
+  }
+
+  // Update the value of mCompositedFrameInvalid.
+  if (mIsCurrentlyDecoded || aAnimationFinished) {
     // Animated images that have finished their animation (ie because it is a
     // finite length animation) don't have RequestRefresh called on them, and so
     // mCompositedFrameInvalid would never get cleared. We clear it here (and
     // also in RasterImage::Decode when we create a decoder for an image that
     // has finished animated so it can display sooner than waiting until the
-    // decode completes). This is safe to do for images that aren't finished
-    // animating because before we paint the refresh driver will call into us
-    // to advance to the correct frame, and that will succeed because we have
-    // all the frames.
+    // decode completes). We also do it if we are fully decoded. This is safe
+    // to do for images that aren't finished animating because before we paint
+    // the refresh driver will call into us to advance to the correct frame,
+    // and that will succeed because we have all the frames.
     mCompositedFrameInvalid = false;
+  } else if (aResult.Type() == MatchType::NOT_FOUND ||
+             aResult.Type() == MatchType::PENDING) {
+    if (mHasBeenDecoded) {
+      MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
+      mCompositedFrameInvalid = true;
+    }
   }
-  mHasBeenDecoded = true;
+  // Otherwise don't change the value of mCompositedFrameInvalid, it will be
+  // updated by RequestRefresh.
 }
 
 void
-AnimationState::SetDiscarded(bool aDiscarded)
+AnimationState::NotifyDecodeComplete()
 {
-  if (aDiscarded) {
-    MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-    mIsCurrentlyDecoded = false;
-    mCompositedFrameInvalid = true;
-  }
-  mDiscarded = aDiscarded;
+  mHasBeenDecoded = true;
 }
 
 void
 AnimationState::ResetAnimation()
 {
   mCurrentAnimationFrameIndex = 0;
 }
 
@@ -302,17 +346,19 @@ FrameAnimator::AdvanceFrame(AnimationSta
 
   // If we're here, we successfully advanced the frame.
   ret.mFrameAdvanced = true;
 
   return ret;
 }
 
 RefreshResult
-FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
+FrameAnimator::RequestRefresh(AnimationState& aState,
+                              const TimeStamp& aTime,
+                              bool aAnimationFinished)
 {
   // By default, an empty RefreshResult.
   RefreshResult ret;
 
   if (aState.IsDiscarded()) {
     return ret;
   }
 
@@ -321,22 +367,18 @@ FrameAnimator::RequestRefresh(AnimationS
   // must easier to reason about then trying to write code that is safe to
   // having the surface disappear at anytime.
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           DefaultSurfaceFlags(),
                                           PlaybackType::eAnimated));
 
-  if (!result) {
-    if (result.Type() == MatchType::NOT_FOUND) {
-      // No surface, and nothing pending, must have been discarded but
-      // we haven't been notified yet.
-      aState.SetDiscarded(true);
-    }
+  aState.UpdateStateInternal(result, aAnimationFinished);
+  if (aState.IsDiscarded() || !result) {
     return ret;
   }
 
   // only advance the frame if the current time is greater than or
   // equal to the current frame's end time.
   Maybe<TimeStamp> currentFrameEndTime =
     GetCurrentImgFrameEndTime(aState, result.Surface());
   if (currentFrameEndTime.isNothing()) {
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -35,32 +35,39 @@ public:
     , mAnimationMode(aAnimationMode)
     , mHasBeenDecoded(false)
     , mIsCurrentlyDecoded(false)
     , mCompositedFrameInvalid(false)
     , mDiscarded(false)
   { }
 
   /**
+   * Call this whenever a decode completes, a decode starts, or the image is
+   * discarded. It will update the internal state. Specifically mDiscarded,
+   * mCompositedFrameInvalid, and mIsCurrentlyDecoded.
+   */
+  void UpdateState(bool aAnimationFinished,
+                   RasterImage *aImage,
+                   const gfx::IntSize& aSize);
+private:
+  void UpdateStateInternal(LookupResult& aResult,
+                           bool aAnimationFinished);
+
+public:
+  /**
    * Call when a decode of this image has been completed.
    */
   void NotifyDecodeComplete();
 
   /**
    * Returns true if this image has been fully decoded before.
    */
   bool GetHasBeenDecoded() { return mHasBeenDecoded; }
 
   /**
-   * Call this with true when this image is discarded. Call this with false
-   * when a decoder is created to decode the image.
-   */
-  void SetDiscarded(bool aDiscarded);
-
-  /**
    * Returns true if this image has been discarded and a decoded has not yet
    * been created to redecode it.
    */
   bool IsDiscarded() { return mDiscarded; }
 
   /**
    * Sets the composited frame as valid or invalid.
    */
@@ -271,17 +278,19 @@ public:
 
   /**
    * Re-evaluate what frame we're supposed to be on, and do whatever blending
    * is necessary to get us to that frame.
    *
    * Returns the result of that blending, including whether the current frame
    * changed and what the resulting dirty rectangle is.
    */
-  RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime);
+  RefreshResult RequestRefresh(AnimationState& aState,
+                               const TimeStamp& aTime,
+                               bool aAnimationFinished);
 
   /**
    * Get the full frame for the current frame of the animation (it may or may
    * not have required compositing). It may not be available because it hasn't
    * been decoded yet, in which case we return an empty LookupResult.
    */
   LookupResult GetCompositedFrame(AnimationState& aState);
 
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -168,17 +168,17 @@ RasterImage::RequestRefresh(const TimeSt
 
   if (!mAnimating) {
     return;
   }
 
   RefreshResult res;
   if (mAnimationState) {
     MOZ_ASSERT(mFrameAnimator);
-    res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime);
+    res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime, mAnimationFinished);
   }
 
   if (res.mFrameAdvanced) {
     // Notify listeners that our frame has actually changed, but do this only
     // once for all frames that we've now passed (if AdvanceFrame() was called
     // more than once).
     #ifdef DEBUG
       mFramesNotified++;
@@ -445,17 +445,17 @@ RasterImage::OnSurfaceDiscarded(const Su
 {
   MOZ_ASSERT(mProgressTracker);
 
   bool animatedFramesDiscarded =
     mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
 
   if (animatedFramesDiscarded && NS_IsMainThread()) {
     MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-    mAnimationState->SetDiscarded(true);
+    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
     // We don't need OnSurfaceDiscardedInternal to handle the animated frames
     // being discarded because we just did.
     animatedFramesDiscarded = false;
   }
 
   RefPtr<RasterImage> image = this;
   NS_DispatchToMainThread(NS_NewRunnableFunction(
                             "RasterImage::OnSurfaceDiscarded",
@@ -466,17 +466,17 @@ RasterImage::OnSurfaceDiscarded(const Su
 
 void
 RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aAnimatedFramesDiscarded && mAnimationState) {
     MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-    mAnimationState->SetDiscarded(true);
+    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
   }
 
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
   }
 }
 
 //******************************************************************************
@@ -1076,17 +1076,17 @@ RasterImage::Discard()
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
   MOZ_ASSERT(!mAnimationState, "Asked to discard for animated image");
 
   // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   if (mAnimationState) {
-    mAnimationState->SetDiscarded(true);
+    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
   }
 
   // Notify that we discarded.
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
   }
 }
 
@@ -1248,20 +1248,20 @@ RasterImage::Decode(const IntSize& aSize
   }
 
   // Create a decoder.
   RefPtr<IDecodingTask> task;
   if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
     task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
                                                   mSourceBuffer, mSize,
                                                   decoderFlags, surfaceFlags);
-    mAnimationState->SetDiscarded(false);
+    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
     // If the animation is finished we can draw right away because we just draw
     // the final frame all the time from now on. See comment in
-    // AnimationState::NotifyDecodeComplete.
+    // AnimationState::UpdateState.
     if (mAnimationFinished) {
       mAnimationState->SetCompositedFrameInvalid(false);
     }
   } else {
     task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
                                          mSourceBuffer, mSize, aSize,
                                          decoderFlags, surfaceFlags);
   }
@@ -1712,16 +1712,17 @@ RasterImage::NotifyDecodeComplete(const 
   NotifyProgress(aProgress, aInvalidRect, aFrameCount,
                  aDecoderFlags, aSurfaceFlags);
 
   if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
       mHasBeenDecoded && mAnimationState) {
     // We've finished a full decode of all animation frames and our AnimationState
     // has been notified about them all, so let it know not to expect anymore.
     mAnimationState->NotifyDecodeComplete();
+    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
   }
 
   // Do some telemetry if this isn't a metadata decode.
   if (!aStatus.mWasMetadataDecode) {
     if (aTelemetry.mChunkCount) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, aTelemetry.mChunkCount);
     }
 
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -126,34 +126,44 @@ NewPromiseAllDataHolder(JSContext* cx, H
 
     dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise));
     dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1));
     dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArray);
     dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectValue(*resolve));
     return dataHolder;
 }
 
+/**
+ * Wrapper for GetAndClearException that handles cases where no exception is
+ * pending, but an error occurred. This can be the case if an OOM was
+ * encountered while throwing the error.
+ */
+static bool
+MaybeGetAndClearException(JSContext* cx, MutableHandleValue rval)
+{
+    if (!cx->isExceptionPending())
+        return false;
+
+    return GetAndClearException(cx, rval);
+}
+
 static MOZ_MUST_USE bool RunResolutionFunction(JSContext *cx, HandleObject resolutionFun,
                                                HandleValue result, ResolutionMode mode,
                                                HandleObject promiseObj);
 
 // ES2016, 25.4.1.1.1, Steps 1.a-b.
 // Extracting all of this internal spec algorithm into a helper function would
 // be tedious, so the check in step 1 and the entirety of step 2 aren't
 // included.
 static bool
 AbruptRejectPromise(JSContext *cx, CallArgs& args, HandleObject promiseObj, HandleObject reject)
 {
-    // Not much we can do about uncatchable exceptions, so just bail.
-    if (!cx->isExceptionPending())
-        return false;
-
     // Step 1.a.
     RootedValue reason(cx);
-    if (!GetAndClearException(cx, &reason))
+    if (!MaybeGetAndClearException(cx, &reason))
         return false;
 
     if (!RunResolutionFunction(cx, reject, reason, RejectMode, promiseObj))
         return false;
 
     // Step 1.b.
     args.rval().setObject(*promiseObj);
     return true;
@@ -346,30 +356,31 @@ ResolvePromiseInternal(JSContext* cx, Ha
     RootedObject resolution(cx, &resolutionVal.toObject());
 
     // Step 6.
     if (resolution == promise) {
         // Step 6.a.
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF);
         RootedValue selfResolutionError(cx);
-        MOZ_ALWAYS_TRUE(GetAndClearException(cx, &selfResolutionError));
+        if (!MaybeGetAndClearException(cx, &selfResolutionError))
+            return false;
 
         // Step 6.b.
         return RejectMaybeWrappedPromise(cx, promise, selfResolutionError);
     }
 
     // Step 8.
     RootedValue thenVal(cx);
     bool status = GetProperty(cx, resolution, resolution, cx->names().then, &thenVal);
 
     // Step 9.
     if (!status) {
         RootedValue error(cx);
-        if (!GetAndClearException(cx, &error))
+        if (!MaybeGetAndClearException(cx, &error))
             return false;
 
         return RejectMaybeWrappedPromise(cx, promise, error);
     }
 
     // Step 10 (implicit).
 
     // Step 11.
@@ -894,19 +905,17 @@ PromiseReactionJob(JSContext* cx, unsign
             handlerResult = argument;
         }
     } else {
         // Step 6.
         FixedInvokeArgs<1> args2(cx);
         args2[0].set(argument);
         if (!Call(cx, handlerVal, UndefinedHandleValue, args2, &handlerResult)) {
             resolutionMode = RejectMode;
-            // Not much we can do about uncatchable exceptions, so just bail
-            // for those.
-            if (!cx->isExceptionPending() || !GetAndClearException(cx, &handlerResult))
+            if (!MaybeGetAndClearException(cx, &handlerResult))
                 return false;
         }
     }
 
     // Steps 7-9.
     size_t hookSlot = resolutionMode == RejectMode
                       ? ReactionRecordSlot_Reject
                       : ReactionRecordSlot_Resolve;
@@ -973,17 +982,17 @@ PromiseResolveThenableJob(JSContext* cx,
     args2[1].set(rejectVal);
 
     RootedValue rval(cx);
 
     // In difference to the usual pattern, we return immediately on success.
     if (Call(cx, then, thenable, args2, &rval))
         return true;
 
-    if (!GetAndClearException(cx, &rval))
+    if (!MaybeGetAndClearException(cx, &rval))
         return false;
 
     FixedInvokeArgs<1> rejectArgs(cx);
     rejectArgs[0].set(rval);
 
     return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
 }
 
@@ -1316,19 +1325,17 @@ PromiseObject::create(JSContext* cx, Han
 
         RootedValue calleeOrRval(cx, ObjectValue(*executor));
         success = Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval);
     }
 
     // Step 10.
     if (!success) {
         RootedValue exceptionVal(cx);
-        // Not much we can do about uncatchable exceptions, so just bail
-        // for those.
-        if (!cx->isExceptionPending() || !GetAndClearException(cx, &exceptionVal))
+        if (!MaybeGetAndClearException(cx, &exceptionVal))
             return nullptr;
 
         FixedInvokeArgs<1> args(cx);
 
         args[0].set(exceptionVal);
 
         // |rejectVal| is unused after this, so we can safely write to it.
         if (!Call(cx, rejectVal, UndefinedHandleValue, args, &rejectVal))
@@ -2120,23 +2127,19 @@ js::CreatePromiseObjectForAsync(JSContex
     promise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
     return promise;
 }
 
 // Async Functions proposal 2.2 steps 3.f, 3.g.
 MOZ_MUST_USE bool
 js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
 {
-    // Not much we can do about uncatchable exceptions, so just bail.
-    if (!cx->isExceptionPending())
-        return false;
-
     // Step 3.f.
     RootedValue exc(cx);
-    if (!GetAndClearException(cx, &exc))
+    if (!MaybeGetAndClearException(cx, &exc))
         return false;
 
     if (!RejectMaybeWrappedPromise(cx, resultPromise, exc))
         return false;
 
     // Step 3.g.
     return true;
 }