Commit fcac8589 authored by Daniel Varga's avatar Daniel Varga
Browse files

Merge mozilla-central to autoland. a=merge on a CLOSED TREE

parents a4e3aa46 14dc5b7d
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -1284,7 +1284,7 @@ dependencies = [

[[package]]
name = "gleam"
version = "0.6.15"
version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3315,7 +3315,7 @@ dependencies = [
 "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "gleam 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
 "gleam 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3366,7 +3366,7 @@ dependencies = [
 "euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
 "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "gleam 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
 "gleam 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 "nsstring 0.1.0",
 "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3624,7 +3624,7 @@ dependencies = [
"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd"
"checksum gleam 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "43edfa3a4321024d7dac1625cf154ef0c16996a6a384d066480e306ebd39fecc"
"checksum gleam 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "39bb69499005e11b7b7cc0af38404a1bc0f53d954bffa8adcdb6e8d5b14f75d5"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2"
"checksum guid_win 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87261686cc5e35b6584f4c2a430c2b153d8a92ab1ef820c16be34c1df8f5f58b"
+7 −0
Original line number Diff line number Diff line
@@ -4301,6 +4301,13 @@ static void NotifyActivityChanged(nsISupports* aSupports, void* aUnused) {
      do_QueryInterface(aSupports));
  if (objectDocumentActivity) {
    objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
  } else {
    nsCOMPtr<nsIImageLoadingContent> imageLoadingContent(
        do_QueryInterface(aSupports));
    if (imageLoadingContent) {
      auto ilc = static_cast<nsImageLoadingContent*>(imageLoadingContent.get());
      ilc->NotifyOwnerDocumentActivityChanged();
    }
  }
}

+5 −0
Original line number Diff line number Diff line
@@ -163,3 +163,8 @@ DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must not be LOADING or DON
DOM4_MSG_DEF(InvalidStateError, "responseXML is only available if responseType is '' or 'document'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML)
DOM4_MSG_DEF(InvalidStateError, "responseText is only available if responseType is '' or 'text'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT)
DOM4_MSG_DEF(InvalidAccessError, "synchronous XMLHttpRequests do not support timeout and responseType.", NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC)

/* Image decode errors. */
DOM4_MSG_DEF(EncodingError, "Node bound to inactive document.", NS_ERROR_DOM_IMAGE_INACTIVE_DOCUMENT)
DOM4_MSG_DEF(EncodingError, "Invalid image request.", NS_ERROR_DOM_IMAGE_INVALID_REQUEST)
DOM4_MSG_DEF(EncodingError, "Invalid encoded image data.", NS_ERROR_DOM_IMAGE_BROKEN)
+211 −6
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ nsImageLoadingContent::nsImageLoadingContent()
    : mCurrentRequestFlags(0),
      mPendingRequestFlags(0),
      mObserverList(nullptr),
      mOutstandingDecodePromises(0),
      mRequestGeneration(0),
      mImageBlockingStatus(nsIContentPolicy::ACCEPT),
      mLoadingEnabled(true),
      mIsImageStateForced(false),
@@ -120,17 +122,21 @@ nsImageLoadingContent::nsImageLoadingContent()
void nsImageLoadingContent::DestroyImageLoadingContent() {
  // Cancel our requests so they won't hold stale refs to us
  // NB: Don't ask to discard the images here.
  RejectDecodePromises(NS_ERROR_DOM_IMAGE_INVALID_REQUEST);
  ClearCurrentRequest(NS_BINDING_ABORTED);
  ClearPendingRequest(NS_BINDING_ABORTED);
}

nsImageLoadingContent::~nsImageLoadingContent() {
  NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
  MOZ_ASSERT(!mCurrentRequest && !mPendingRequest,
             "DestroyImageLoadingContent not called");
  NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
  MOZ_ASSERT(!mObserverList.mObserver && !mObserverList.mNext,
             "Observers still registered?");
  NS_ASSERTION(mScriptedObservers.IsEmpty(),
  MOZ_ASSERT(mScriptedObservers.IsEmpty(),
             "Scripted observers still registered?");
  MOZ_ASSERT(mOutstandingDecodePromises == 0,
             "Decode promises still unfulfilled?");
  MOZ_ASSERT(mDecodePromises.IsEmpty(), "Decode promises still unfulfilled?");
}

/*
@@ -204,6 +210,11 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest, int32_t aType,
    return OnLoadComplete(aRequest, status);
  }

  if (aType == imgINotificationObserver::FRAME_COMPLETE &&
      mCurrentRequest == aRequest) {
    MaybeResolveDecodePromises();
  }

  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
    nsCOMPtr<imgIContainer> container;
    aRequest->GetImage(getter_AddRefs(container));
@@ -260,6 +271,7 @@ nsresult nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest,
  nsCOMPtr<nsINode> thisNode =
      do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
  SVGObserverUtils::InvalidateDirectRenderingObservers(thisNode->AsElement());
  MaybeResolveDecodePromises();

  return NS_OK;
}
@@ -334,6 +346,166 @@ void nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled) {
  }
}

already_AddRefed<Promise> nsImageLoadingContent::QueueDecodeAsync(
    ErrorResult& aRv) {
  Document* doc = GetOurOwnerDoc();
  RefPtr<Promise> promise = Promise::Create(doc->GetScopeObject(), aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  class QueueDecodeTask final : public Runnable {
   public:
    QueueDecodeTask(nsImageLoadingContent* aOwner, Promise* aPromise,
                    uint32_t aRequestGeneration)
        : Runnable("nsImageLoadingContent::QueueDecodeTask"),
          mOwner(aOwner),
          mPromise(aPromise),
          mRequestGeneration(aRequestGeneration) {}

    NS_IMETHOD Run() override {
      mOwner->DecodeAsync(std::move(mPromise), mRequestGeneration);
      return NS_OK;
    }

   private:
    RefPtr<nsImageLoadingContent> mOwner;
    RefPtr<Promise> mPromise;
    uint32_t mRequestGeneration;
  };

  if (++mOutstandingDecodePromises == 1) {
    MOZ_ASSERT(mDecodePromises.IsEmpty());
    doc->RegisterActivityObserver(this);
  }

  auto task = MakeRefPtr<QueueDecodeTask>(this, promise, mRequestGeneration);
  nsContentUtils::RunInStableState(task.forget());
  return promise.forget();
}

void nsImageLoadingContent::DecodeAsync(RefPtr<Promise>&& aPromise,
                                        uint32_t aRequestGeneration) {
  MOZ_ASSERT(nsContentUtils::IsInStableOrMetaStableState());
  MOZ_ASSERT(aPromise);
  MOZ_ASSERT(mOutstandingDecodePromises > mDecodePromises.Length());

  // The request may have gotten updated since the decode call was issued.
  if (aRequestGeneration != mRequestGeneration) {
    aPromise->MaybeReject(NS_ERROR_DOM_IMAGE_INVALID_REQUEST);
    // We never got placed in mDecodePromises, so we must ensure we decrement
    // the counter explicitly.
    --mOutstandingDecodePromises;
    MaybeDeregisterActivityObserver();
    return;
  }

  bool wasEmpty = mDecodePromises.IsEmpty();
  mDecodePromises.AppendElement(std::move(aPromise));
  if (wasEmpty) {
    MaybeResolveDecodePromises();
  }
}

void nsImageLoadingContent::MaybeResolveDecodePromises() {
  if (mDecodePromises.IsEmpty()) {
    return;
  }

  if (!mCurrentRequest) {
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_INVALID_REQUEST);
    return;
  }

  // Only can resolve if our document is the active document. If not we are
  // supposed to reject the promise, even if it was fulfilled successfully.
  if (!GetOurOwnerDoc()->IsCurrentActiveDocument()) {
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_INACTIVE_DOCUMENT);
    return;
  }

  // If any error occurred while decoding, we need to reject first.
  uint32_t status = imgIRequest::STATUS_NONE;
  mCurrentRequest->GetImageStatus(&status);
  if (status & imgIRequest::STATUS_ERROR) {
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_BROKEN);
    return;
  }

  // We need the size to bother with requesting a decode, as we are either
  // blocked on validation or metadata decoding.
  if (!(status & imgIRequest::STATUS_SIZE_AVAILABLE)) {
    return;
  }

  // Check the surface cache status and/or request decoding begin. We do this
  // before LOAD_COMPLETE because we want to start as soon as possible.
  uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING |
                   imgIContainer::FLAG_AVOID_REDECODE_FOR_SIZE;
  if (!mCurrentRequest->RequestDecodeWithResult(flags)) {
    return;
  }

  // We can only fulfill the promises once we have all the data.
  if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
    return;
  }

  for (auto& promise : mDecodePromises) {
    promise->MaybeResolveWithUndefined();
  }

  MOZ_ASSERT(mOutstandingDecodePromises >= mDecodePromises.Length());
  mOutstandingDecodePromises -= mDecodePromises.Length();
  mDecodePromises.Clear();
  MaybeDeregisterActivityObserver();
}

void nsImageLoadingContent::RejectDecodePromises(nsresult aStatus) {
  if (mDecodePromises.IsEmpty()) {
    return;
  }

  for (auto& promise : mDecodePromises) {
    promise->MaybeReject(aStatus);
  }

  MOZ_ASSERT(mOutstandingDecodePromises >= mDecodePromises.Length());
  mOutstandingDecodePromises -= mDecodePromises.Length();
  mDecodePromises.Clear();
  MaybeDeregisterActivityObserver();
}

void nsImageLoadingContent::MaybeAgeRequestGeneration(nsIURI* aNewURI) {
  MOZ_ASSERT(mCurrentRequest);

  // If the current request is about to change, we need to verify if the new
  // URI matches the existing current request's URI. If it doesn't, we need to
  // reject any outstanding promises due to the current request mutating as per
  // step 2.2 of the decode API requirements.
  //
  // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
  if (aNewURI) {
    nsCOMPtr<nsIURI> currentURI;
    mCurrentRequest->GetURI(getter_AddRefs(currentURI));

    bool equal = false;
    if (NS_SUCCEEDED(aNewURI->Equals(currentURI, &equal)) && equal) {
      return;
    }
  }

  ++mRequestGeneration;
  RejectDecodePromises(NS_ERROR_DOM_IMAGE_INVALID_REQUEST);
}

void nsImageLoadingContent::MaybeDeregisterActivityObserver() {
  if (mOutstandingDecodePromises == 0) {
    MOZ_ASSERT(mDecodePromises.IsEmpty());
    GetOurOwnerDoc()->UnregisterActivityObserver(this);
  }
}

void nsImageLoadingContent::SetSyncDecodingHint(bool aHint) {
  if (mSyncDecodingHint == aHint) {
    return;
@@ -786,6 +958,15 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
  // Shouldn't that be done before the start of the load?
  // XXX what about shouldProcess?

  // If we have a current request without a size, we know we will replace it
  // with the PrepareNextRequest below. If the new current request is for a
  // different URI, then we need to reject any outstanding promises.
  if (mCurrentRequest && !HaveSize(mCurrentRequest)) {
    nsCOMPtr<nsIURI> uri;
    aChannel->GetOriginalURI(getter_AddRefs(uri));
    MaybeAgeRequestGeneration(uri);
  }

  // Our state might change. Watch it.
  AutoStateChanger changer(this, true);

@@ -944,6 +1125,13 @@ nsresult nsImageLoadingContent::LoadImage(nsIURI* aNewURI, bool aForce,
    }
  }

  // If we have a current request without a size, we know we will replace it
  // with the PrepareNextRequest below. If the new current request is for a
  // different URI, then we need to reject any outstanding promises.
  if (mCurrentRequest && !HaveSize(mCurrentRequest)) {
    MaybeAgeRequestGeneration(aNewURI);
  }

  // From this point on, our image state could change. Watch it.
  AutoStateChanger changer(this, aNotify);

@@ -1120,11 +1308,13 @@ void nsImageLoadingContent::UpdateImageState(bool aNotify) {
  } else if (!mCurrentRequest) {
    // No current request means error, since we weren't disabled or suppressed
    mBroken = true;
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_BROKEN);
  } else {
    uint32_t currentLoadStatus;
    nsresult rv = mCurrentRequest->GetImageStatus(&currentLoadStatus);
    if (NS_FAILED(rv) || (currentLoadStatus & imgIRequest::STATUS_ERROR)) {
      mBroken = true;
      RejectDecodePromises(NS_ERROR_DOM_IMAGE_BROKEN);
    } else if (!(currentLoadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
      mLoading = true;
    }
@@ -1135,6 +1325,7 @@ void nsImageLoadingContent::UpdateImageState(bool aNotify) {
}

void nsImageLoadingContent::CancelImageRequests(bool aNotify) {
  RejectDecodePromises(NS_ERROR_DOM_IMAGE_INVALID_REQUEST);
  AutoStateChanger changer(this, aNotify);
  ClearPendingRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
  ClearCurrentRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
@@ -1183,6 +1374,7 @@ nsresult nsImageLoadingContent::FireEvent(const nsAString& aEventType,
                                          bool aIsCancelable) {
  if (nsContentUtils::DocumentInactiveForImageLoads(GetOurOwnerDoc())) {
    // Don't bother to fire any events, especially error events.
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_INACTIVE_DOCUMENT);
    return NS_OK;
  }

@@ -1315,6 +1507,13 @@ class ImageRequestAutoLock {
void nsImageLoadingContent::MakePendingRequestCurrent() {
  MOZ_ASSERT(mPendingRequest);

  // If we have a pending request, we know that there is an existing current
  // request with size information. If the pending request is for a different
  // URI, then we need to reject any outstanding promises.
  nsCOMPtr<nsIURI> uri;
  mPendingRequest->GetURI(getter_AddRefs(uri));
  MaybeAgeRequestGeneration(uri);

  // Lock mCurrentRequest for the duration of this method.  We do this because
  // PrepareCurrentRequest() might unlock mCurrentRequest.  If mCurrentRequest
  // and mPendingRequest are both requests for the same image, unlocking
@@ -1408,6 +1607,12 @@ bool nsImageLoadingContent::HaveSize(imgIRequest* aImage) {
  return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE));
}

void nsImageLoadingContent::NotifyOwnerDocumentActivityChanged() {
  if (!GetOurOwnerDoc()->IsCurrentActiveDocument()) {
    RejectDecodePromises(NS_ERROR_DOM_IMAGE_INACTIVE_DOCUMENT);
  }
}

void nsImageLoadingContent::BindToTree(Document* aDocument, nsIContent* aParent,
                                       nsIContent* aBindingParent) {
  // We may be getting connected, if so our image should be tracked,
+74 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "mozilla/ErrorResult.h"
#include "nsIContentPolicy.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "nsAttrValue.h"

@@ -82,6 +83,12 @@ class nsImageLoadingContent : public nsIImageLoadingContent {
   */
  void SetSyncDecodingHint(bool aHint);

  /**
   * Notify us that the document state has changed. Called by nsDocument so that
   * we may reject any promises which require the document to be active.
   */
  void NotifyOwnerDocumentActivityChanged();

 protected:
  enum ImageLoadType {
    // Most normal image loads
@@ -230,6 +237,18 @@ class nsImageLoadingContent : public nsIImageLoadingContent {
  uint32_t NaturalWidth();
  uint32_t NaturalHeight();

  /**
   * Create a promise and queue a microtask which will ensure the current
   * request (after any pending loads are applied) has requested a full decode.
   * The promise is fulfilled once the request has a fully decoded surface that
   * is available for drawing, or an error condition occurrs (e.g. broken image,
   * current request is updated, etc).
   *
   * https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
   */
  already_AddRefed<mozilla::dom::Promise> QueueDecodeAsync(
      mozilla::ErrorResult& aRv);

  enum class ImageDecodingType : uint8_t {
    Auto,
    Async,
@@ -240,6 +259,38 @@ class nsImageLoadingContent : public nsIImageLoadingContent {
  static const nsAttrValue::EnumTable* kDecodingTableDefault;

 private:
  /**
   * Enqueue and/or fulfill a promise created by QueueDecodeAsync.
   */
  void DecodeAsync(RefPtr<mozilla::dom::Promise>&& aPromise,
                   uint32_t aRequestGeneration);

  /**
   * Attempt to resolve all queued promises based on the state of the current
   * request. If the current request does not yet have all of the encoded data,
   * or the decoding has not yet completed, it will return without changing the
   * promise states.
   */
  void MaybeResolveDecodePromises();

  /**
   * Reject all queued promises with the given status.
   */
  void RejectDecodePromises(nsresult aStatus);

  /**
   * Age the generation counter if we have a new current request with a
   * different URI. If the generation counter is aged, then all queued promises
   * will also be rejected.
   */
  void MaybeAgeRequestGeneration(nsIURI* aNewURI);

  /**
   * Deregister as an observer for the owner document's activity notifications
   * if we have no outstanding decode promises.
   */
  void MaybeDeregisterActivityObserver();

  /**
   * Struct used to manage the native image observers.
   */
@@ -483,6 +534,12 @@ class nsImageLoadingContent : public nsIImageLoadingContent {
   */
  nsTArray<RefPtr<ScriptedImageObserver>> mScriptedObservers;

  /**
   * Promises created by QueueDecodeAsync that are still waiting to be
   * fulfilled by the image being fully decoded.
   */
  nsTArray<RefPtr<mozilla::dom::Promise>> mDecodePromises;

  /**
   * When mIsImageStateForced is true, this holds the ImageState that we'll
   * return in ImageState().
@@ -491,6 +548,23 @@ class nsImageLoadingContent : public nsIImageLoadingContent {

  mozilla::TimeStamp mMostRecentRequestChange;

  /**
   * Total number of outstanding decode promises, including those stored in
   * mDecodePromises and those embedded in runnables waiting to be enqueued.
   * This is used to determine whether we need to register as an observer for
   * document activity notifications.
   */
  size_t mOutstandingDecodePromises;

  /**
   * An incrementing counter representing the current request generation;
   * Each time mCurrentRequest is modified with a different URI, this will
   * be incremented. Each QueueDecodeAsync call will cache the generation
   * of the current request so that when it is processed, it knows if it
   * should have rejected because the request changed.
   */
  uint32_t mRequestGeneration;

  int16_t mImageBlockingStatus;
  bool mLoadingEnabled : 1;

Loading