diff --git a/content/base/public/nsIImageLoadingContent.idl b/content/base/public/nsIImageLoadingContent.idl index 192e3b52370..9a08f47c76f 100644 --- a/content/base/public/nsIImageLoadingContent.idl +++ b/content/base/public/nsIImageLoadingContent.idl @@ -34,7 +34,7 @@ interface nsIFrame; * sufficient, when combined with the imageBlockingStatus information.) */ -[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)] +[scriptable, uuid(4bf1a7c5-6edb-4191-a257-e31a90f6aa85)] interface nsIImageLoadingContent : imgIDecoderObserver { /** @@ -156,9 +156,4 @@ interface nsIImageLoadingContent : imgIDecoderObserver * as PR_FALSE to revert ImageState() to its original behaviour. */ void forceImageState(in boolean aForce, in unsigned long long aState); - - /** - * We need to be notified when our document changes. - */ - [noscript, notxpcom] void NotifyOwnerDocumentChanged(in nsIDocument aOldDoc); }; diff --git a/content/base/src/nsGenConImageContent.cpp b/content/base/src/nsGenConImageContent.cpp index bfa80698240..1f55fcf7e0e 100644 --- a/content/base/src/nsGenConImageContent.cpp +++ b/content/base/src/nsGenConImageContent.cpp @@ -35,6 +35,10 @@ public: } // nsIContent overrides + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers); + virtual void UnbindFromTree(bool aDeep, bool aNullParent); virtual nsEventStates IntrinsicState() const; private: @@ -67,6 +71,28 @@ nsGenConImageContent::~nsGenConImageContent() DestroyImageLoadingContent(); } +nsresult +nsGenConImageContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv; + rv = nsXMLElement::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + return NS_OK; +} + +void +nsGenConImageContent::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + nsXMLElement::UnbindFromTree(aDeep, aNullParent); +} + nsEventStates nsGenConImageContent::IntrinsicState() const { diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 8d1627e4623..0b84507503f 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -72,7 +72,9 @@ static void PrintReqURL(imgIRequest* req) { nsImageLoadingContent::nsImageLoadingContent() - : mObserverList(nullptr), + : mCurrentRequestFlags(0), + mPendingRequestFlags(0), + mObserverList(nullptr), mImageBlockingStatus(nsIContentPolicy::ACCEPT), mLoadingEnabled(true), mIsImageStateForced(false), @@ -83,8 +85,6 @@ nsImageLoadingContent::nsImageLoadingContent() mSuppressed(false), mBlockingOnload(false), mNewRequestsWillNeedAnimationReset(false), - mPendingRequestNeedsResetAnimation(false), - mCurrentRequestNeedsResetAnimation(false), mStateChangerDepth(0), mCurrentRequestRegistered(false), mPendingRequestRegistered(false) @@ -292,7 +292,9 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest, // initial paint delay (for example, being in the bfcache), but we probably // aren't loading images in those situations. - nsIDocument* doc = GetOurDocument(); + // XXXkhuey should this be GetOurCurrentDoc? Decoding if we're not in + // the document seems silly. + nsIDocument* doc = GetOurOwnerDoc(); nsIPresShell* shell = doc ? doc->GetShell() : nullptr; if (shell && shell->IsVisible() && (!shell->DidInitialReflow() || shell->IsPaintingSuppressed())) { @@ -551,7 +553,7 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel, return NS_ERROR_NULL_POINTER; } - nsCOMPtr doc = GetOurDocument(); + nsCOMPtr doc = GetOurOwnerDoc(); if (!doc) { // Don't bother return NS_OK; @@ -600,29 +602,13 @@ NS_IMETHODIMP nsImageLoadingContent::ForceReload() * Non-interface methods */ -void -nsImageLoadingContent::NotifyOwnerDocumentChanged(nsIDocument *aOldDoc) -{ - // If we had a document before, unregister ourselves with it. - if (aOldDoc) { - if (mCurrentRequest) - aOldDoc->RemoveImage(mCurrentRequest); - if (mPendingRequest) - aOldDoc->RemoveImage(mPendingRequest); - } - - // Re-track the images - TrackImage(mCurrentRequest); - TrackImage(mPendingRequest); -} - nsresult nsImageLoadingContent::LoadImage(const nsAString& aNewURI, bool aForce, bool aNotify) { // First, get a document (needed for security checks and the like) - nsIDocument* doc = GetOurDocument(); + nsIDocument* doc = GetOurOwnerDoc(); if (!doc) { // No reason to bother, I think... return NS_OK; @@ -670,11 +656,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, return NS_OK; } - NS_ASSERTION(!aDocument || aDocument == GetOurDocument(), + NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(), "Bogus document passed in"); // First, get a document (needed for security checks and the like) if (!aDocument) { - aDocument = GetOurDocument(); + aDocument = GetOurOwnerDoc(); if (!aDocument) { // No reason to bother, I think... return NS_OK; @@ -882,7 +868,7 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest, } nsIDocument* -nsImageLoadingContent::GetOurDocument() +nsImageLoadingContent::GetOurOwnerDoc() { nsCOMPtr thisContent = do_QueryInterface(this); NS_ENSURE_TRUE(thisContent, nullptr); @@ -890,6 +876,15 @@ nsImageLoadingContent::GetOurDocument() return thisContent->OwnerDoc(); } +nsIDocument* +nsImageLoadingContent::GetOurCurrentDoc() +{ + nsCOMPtr thisContent = do_QueryInterface(this); + NS_ENSURE_TRUE(thisContent, nullptr); + + return thisContent->GetCurrentDoc(); +} + nsIFrame* nsImageLoadingContent::GetOurPrimaryFrame() { @@ -996,7 +991,9 @@ nsImageLoadingContent::PrepareCurrentRequest() // Get rid of anything that was there previously. ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED); - mCurrentRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset; + if (mNewRequestsWillNeedAnimationReset) { + mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; + } // Return a reference. return mCurrentRequest; @@ -1008,7 +1005,9 @@ nsImageLoadingContent::PreparePendingRequest() // Get rid of anything that was there previously. ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED); - mPendingRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset; + if (mNewRequestsWillNeedAnimationReset) { + mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; + } // Return a reference. return mPendingRequest; @@ -1054,8 +1053,8 @@ nsImageLoadingContent::MakePendingRequestCurrent() PrepareCurrentRequest() = mPendingRequest; mPendingRequest = nullptr; - mCurrentRequestNeedsResetAnimation = mPendingRequestNeedsResetAnimation; - mPendingRequestNeedsResetAnimation = false; + mCurrentRequestFlags = mPendingRequestFlags; + mPendingRequestFlags = 0; ResetAnimationIfNeeded(); } @@ -1080,7 +1079,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason) UntrackImage(mCurrentRequest); mCurrentRequest->CancelAndForgetObserver(aReason); mCurrentRequest = nullptr; - mCurrentRequestNeedsResetAnimation = false; + mCurrentRequestFlags = 0; // We only block onload during the decoding of "current" images. This one is // going away, so we should unblock unconditionally here. @@ -1107,7 +1106,7 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason) UntrackImage(mPendingRequest); mPendingRequest->CancelAndForgetObserver(aReason); mPendingRequest = nullptr; - mPendingRequestNeedsResetAnimation = false; + mPendingRequestFlags = 0; } bool* @@ -1125,12 +1124,13 @@ nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest) void nsImageLoadingContent::ResetAnimationIfNeeded() { - if (mCurrentRequest && mCurrentRequestNeedsResetAnimation) { + if (mCurrentRequest && + (mCurrentRequestFlags & REQUEST_NEEDS_ANIMATION_RESET)) { nsCOMPtr container; mCurrentRequest->GetImage(getter_AddRefs(container)); if (container) container->ResetAnimation(); - mCurrentRequestNeedsResetAnimation = false; + mCurrentRequestFlags &= ~REQUEST_NEEDS_ANIMATION_RESET; } } @@ -1155,7 +1155,7 @@ nsImageLoadingContent::SetBlockingOnload(bool aBlocking) return; // Get the document - nsIDocument* doc = GetOurDocument(); + nsIDocument* doc = GetOurOwnerDoc(); if (doc) { // Take the appropriate action @@ -1169,13 +1169,51 @@ nsImageLoadingContent::SetBlockingOnload(bool aBlocking) } } +void +nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + // We may be entering the document, so if our image should be tracked, + // track it. + if (!aDocument) + return; + + if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED) + aDocument->AddImage(mCurrentRequest); + if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED) + aDocument->AddImage(mPendingRequest); +} + +void +nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) +{ + // We may be leaving the document, so if our image is tracked, untrack it. + nsCOMPtr doc = GetOurCurrentDoc(); + if (!doc) + return; + + if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED) + doc->RemoveImage(mCurrentRequest); + if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED) + doc->RemoveImage(mPendingRequest); +} + nsresult nsImageLoadingContent::TrackImage(imgIRequest* aImage) { if (!aImage) return NS_OK; - nsIDocument* doc = GetOurDocument(); + MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest, + "Why haven't we heard of this request?"); + if (aImage == mCurrentRequest) { + mCurrentRequestFlags |= REQUEST_SHOULD_BE_TRACKED; + } else { + mPendingRequestFlags |= REQUEST_SHOULD_BE_TRACKED; + } + + nsIDocument* doc = GetOurCurrentDoc(); if (doc) return doc->AddImage(aImage); return NS_OK; @@ -1187,10 +1225,18 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage) if (!aImage) return NS_OK; + MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest, + "Why haven't we heard of this request?"); + if (aImage == mCurrentRequest) { + mCurrentRequestFlags &= ~REQUEST_SHOULD_BE_TRACKED; + } else { + mPendingRequestFlags &= ~REQUEST_SHOULD_BE_TRACKED; + } + // If GetOurDocument() returns null here, we've outlived our document. // That's fine, because the document empties out the tracker and unlocks // all locked images on destruction. - nsIDocument* doc = GetOurDocument(); + nsIDocument* doc = GetOurCurrentDoc(); if (doc) return doc->RemoveImage(aImage); return NS_OK; diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 837c4aeefae..1bd0a2f4e4c 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -87,13 +87,14 @@ protected: nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL); /** - * helper to get the document for this content (from the nodeinfo - * and such). Not named GetDocument to prevent ambiguous method - * names in subclasses + * helpers to get the document for this content (from the nodeinfo + * and such). Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous + * method names in subclasses * * @return the document we belong to */ - nsIDocument* GetOurDocument(); + nsIDocument* GetOurOwnerDoc(); + nsIDocument* GetOurCurrentDoc(); /** * Helper function to get the frame associated with this content. Not named @@ -152,6 +153,11 @@ protected: */ virtual mozilla::CORSMode GetCORSMode(); + // Subclasses are *required* to call BindToTree/UnbindFromTree. + void BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, bool aCompileEventHandlers); + void UnbindFromTree(bool aDeep, bool aNullParent); + private: /** * Struct used to manage the image observers. @@ -308,6 +314,16 @@ protected: /* MEMBERS */ nsCOMPtr mCurrentRequest; nsCOMPtr mPendingRequest; + PRUint32 mCurrentRequestFlags; + PRUint32 mPendingRequestFlags; + + enum { + // Set if the request needs + REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U, + // Set if the request should be tracked. This is true if the request is + // not tracked iff this node is not in the document. + REQUEST_SHOULD_BE_TRACKED = 0x00000002U + }; // If the image was blocked or if there was an error loading, it's nice to // still keep track of what the URI was despite not having an imgIRequest. @@ -366,9 +382,6 @@ protected: bool mNewRequestsWillNeedAnimationReset : 1; private: - bool mPendingRequestNeedsResetAnimation : 1; - bool mCurrentRequestNeedsResetAnimation : 1; - /* The number of nested AutoStateChangers currently tracking our state. */ PRUint8 mStateChangerDepth; diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index 4334649cb01..653fd9001c8 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -510,16 +510,9 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, olc->NotifyOwnerDocumentActivityChanged(); } } - - // nsImageLoadingContent needs to know when its document changes - if (oldDoc != newDoc) { - nsCOMPtr imageContent(do_QueryInterface(aNode)); - if (imageContent) { - imageContent->NotifyOwnerDocumentChanged(oldDoc); - } - if (oldDoc->MayHaveDOMMutationObservers()) { - newDoc->SetMayHaveDOMMutationObservers(); - } + + if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) { + newDoc->SetMayHaveDOMMutationObservers(); } if (elem) { diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 2f485b1f663..9e24e60a0eb 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -605,10 +605,13 @@ nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType) nsresult nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, - nsIContent* /*aParent*/, - nsIContent* /*aBindingParent*/, - bool /*aCompileEventHandlers*/) + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) { + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + if (aDocument) { return aDocument->AddPlugin(this); } @@ -616,8 +619,10 @@ nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, } void -nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/) +nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) { + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); MOZ_ASSERT(thisContent); diff --git a/content/html/content/src/nsHTMLImageElement.cpp b/content/html/content/src/nsHTMLImageElement.cpp index 251aac465af..3673a8113a6 100644 --- a/content/html/content/src/nsHTMLImageElement.cpp +++ b/content/html/content/src/nsHTMLImageElement.cpp @@ -407,6 +407,9 @@ nsHTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { // FIXME: Bug 660963 it would be nice if we could just have // ClearBrokenState update our state and do it fast... @@ -424,6 +427,13 @@ nsHTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, return rv; } +void +nsHTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); +} + void nsHTMLImageElement::MaybeLoadImage() { diff --git a/content/html/content/src/nsHTMLImageElement.h b/content/html/content/src/nsHTMLImageElement.h index 55915e5f4a9..e648f1c564f 100644 --- a/content/html/content/src/nsHTMLImageElement.h +++ b/content/html/content/src/nsHTMLImageElement.h @@ -91,6 +91,7 @@ public: virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers); + virtual void UnbindFromTree(bool aDeep, bool aNullParent); virtual nsEventStates IntrinsicState() const; virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index d029f397672..76f87f991e3 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -2495,6 +2495,9 @@ nsHTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + if (mType == NS_FORM_INPUT_IMAGE) { // Our base URI may have changed; claim that our URI changed, and the // nsImageLoadingContent will decide whether a new image load is warranted. @@ -2541,6 +2544,7 @@ nsHTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent) WillRemoveFromRadioGroup(); } + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent); // GetCurrentDoc is returning nullptr so we can update the value diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index 7d5a38b8f26..1cb746974c4 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -5604,6 +5604,9 @@ nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + if (mStringAttributes[HREF].IsExplicitlySet()) { // FIXME: Bug 660963 it would be nice if we could just have // ClearBrokenState update our state and do it fast... @@ -5615,6 +5618,13 @@ nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, return rv; } + +void +nsSVGFEImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + nsSVGFEImageElementBase::UnbindFromTree(aDeep, aNullParent); +} nsEventStates nsSVGFEImageElement::IntrinsicState() const diff --git a/content/svg/content/src/nsSVGFilters.h b/content/svg/content/src/nsSVGFilters.h index 1114217c31d..2eb932ccae9 100644 --- a/content/svg/content/src/nsSVGFilters.h +++ b/content/svg/content/src/nsSVGFilters.h @@ -264,6 +264,7 @@ public: virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers); + virtual void UnbindFromTree(bool aDeep, bool aNullParent); virtual nsEventStates IntrinsicState() const; // imgIDecoderObserver diff --git a/content/svg/content/src/nsSVGImageElement.cpp b/content/svg/content/src/nsSVGImageElement.cpp index 339435b64ca..16f80e25645 100644 --- a/content/svg/content/src/nsSVGImageElement.cpp +++ b/content/svg/content/src/nsSVGImageElement.cpp @@ -183,6 +183,9 @@ nsSVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + if (mStringAttributes[HREF].IsExplicitlySet()) { // FIXME: Bug 660963 it would be nice if we could just have // ClearBrokenState update our state and do it fast... @@ -195,6 +198,13 @@ nsSVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, return rv; } +void +nsSVGImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + nsSVGImageElementBase::UnbindFromTree(aDeep, aNullParent); +} + nsEventStates nsSVGImageElement::IntrinsicState() const { diff --git a/content/svg/content/src/nsSVGImageElement.h b/content/svg/content/src/nsSVGImageElement.h index 334f1b0fdf0..c05d2ef47e8 100644 --- a/content/svg/content/src/nsSVGImageElement.h +++ b/content/svg/content/src/nsSVGImageElement.h @@ -49,6 +49,7 @@ public: virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers); + virtual void UnbindFromTree(bool aDeep, bool aNullParent); virtual nsEventStates IntrinsicState() const;