author | Hiroyuki Ikezoe <hikezoe.birchill@mozilla.com> |
Wed, 12 Feb 2020 21:31:48 +0000 (2020-02-12) | |
changeset 513623 | e27886fc6a2a2b2915bebfbc6f22e494cd11404c |
parent 513622 | 63cb9dca2a0d4740688ae1fccbdebf1ab314fdc7 |
child 513624 | 738e2a3729cf0cfcac567a70aedfdc0ed2b652a9 |
push id | 37118 |
push user | rmaries@mozilla.com |
push date | Thu, 13 Feb 2020 03:57:45 +0000 (2020-02-13) |
treeherder | mozilla-central@2f6870dd1b99 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | emilio, hsivonen |
bugs | 1542784 |
milestone | 75.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -9,16 +9,17 @@ #include "nsIFrame.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" #include "mozilla/PresShell.h" #include "mozilla/ServoBindings.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/HTMLImageElement.h" #include "Units.h" namespace mozilla { namespace dom { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) @@ -115,16 +116,35 @@ already_AddRefed<DOMIntersectionObserver return nullptr; } observer->mThresholds.AppendElement(thresh); } return observer.forget(); } +already_AddRefed<DOMIntersectionObserver> +DOMIntersectionObserver::CreateLazyLoadObserver(nsPIDOMWindowInner* aOwner) { + RefPtr<DOMIntersectionObserver> observer = new DOMIntersectionObserver( + aOwner, + [](const Sequence<OwningNonNull<DOMIntersectionObserverEntry>>& entries) { + for (const auto& entry : entries) { + MOZ_ASSERT(entry->Target()->IsHTMLElement(nsGkAtoms::img)); + if (entry->IsIntersecting()) { + static_cast<HTMLImageElement*>(entry->Target()) + ->StopLazyLoadingAndStartLoadIfNeeded(); + } + } + }); + + observer->mThresholds.AppendElement(std::numeric_limits<double>::min()); + + return observer.forget(); +} + bool DOMIntersectionObserver::SetRootMargin(const nsAString& aString) { return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin); } void DOMIntersectionObserver::GetRootMargin(DOMString& aRetVal) { nsString& retVal = aRetVal; Servo_IntersectionObserverRootMargin_ToString(&mRootMargin, &retVal); }
--- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -80,16 +80,24 @@ class DOMIntersectionObserverEntry final } class DOMIntersectionObserver final : public nsISupports, public nsWrapperCache { virtual ~DOMIntersectionObserver() { Disconnect(); } typedef void (*NativeIntersectionObserverCallback)( const Sequence<OwningNonNull<DOMIntersectionObserverEntry>>& aEntries); + DOMIntersectionObserver(nsPIDOMWindowInner* aOwner, + NativeIntersectionObserverCallback aCb) + : mOwner(aOwner), + mDocument(mOwner->GetExtantDoc()), + mCallback(aCb), + mConnected(false) { + MOZ_ASSERT(mOwner); + } public: DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner, dom::IntersectionCallback& aCb) : mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(RefPtr<dom::IntersectionCallback>(&aCb)), mConnected(false) {} @@ -126,16 +134,19 @@ class DOMIntersectionObserver final : pu return mCallback.as<RefPtr<dom::IntersectionCallback>>(); } bool SetRootMargin(const nsAString& aString); void Update(Document* aDocument, DOMHighResTimeStamp time); MOZ_CAN_RUN_SCRIPT void Notify(); + static already_AddRefed<DOMIntersectionObserver> CreateLazyLoadObserver( + nsPIDOMWindowInner* aOwner); + protected: void Connect(); void QueueIntersectionObserverEntry(Element* aTarget, DOMHighResTimeStamp time, const Maybe<nsRect>& aRootRect, const nsRect& aTargetRect, const Maybe<nsRect>& aIntersectionRect, double aIntersectionRatio);
--- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -2143,16 +2143,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) DocumentOrShadowRoot::Traverse(tmp, cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker) @@ -2250,16 +2251,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Do tmp->DisconnectChild(child); child->UnbindFromTree(); } tmp->UnlinkOriginalDocumentIfStatic(); tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection) NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages); @@ -14659,16 +14661,32 @@ void Document::NotifyIntersectionObserve } for (const auto& observer : observers) { if (observer) { observer->Notify(); } } } +DOMIntersectionObserver* Document::GetLazyLoadImageObserver() { + Document* rootDoc = nsContentUtils::GetRootDocument(this); + MOZ_ASSERT(rootDoc); + + if (rootDoc->mLazyLoadImageObserver) { + return rootDoc->mLazyLoadImageObserver; + } + + if (nsPIDOMWindowInner* inner = rootDoc->GetInnerWindow()) { + rootDoc->mLazyLoadImageObserver = + DOMIntersectionObserver::CreateLazyLoadObserver(inner); + } + + return rootDoc->mLazyLoadImageObserver; +} + static CallState NotifyLayerManagerRecreatedCallback(Document& aDocument, void*) { aDocument.NotifyLayerManagerRecreated(); return CallState::Continue; } void Document::NotifyLayerManagerRecreated() { EnumerateActivityObservers(NotifyActivityChanged, nullptr);
--- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -3607,16 +3607,18 @@ class Document : public nsINode, bool HasIntersectionObservers() const { return !mIntersectionObservers.IsEmpty(); } void UpdateIntersectionObservations(); void ScheduleIntersectionObserverNotification(); MOZ_CAN_RUN_SCRIPT void NotifyIntersectionObservers(); + DOMIntersectionObserver* GetLazyLoadImageObserver(); + // Dispatch a runnable related to the document. nsresult Dispatch(TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) final; virtual nsISerialEventTarget* EventTargetFor( TaskCategory aCategory) const override; virtual AbstractThread* AbstractMainThreadFor( @@ -4863,16 +4865,18 @@ class Document : public nsINode, // Weak reference to the scope object (aka the script global object) // that, unlike mScriptGlobalObject, is never unset once set. This // is a weak reference to avoid leaks due to circular references. nsWeakPtr mScopeObject; // Array of intersection observers nsTHashtable<nsPtrHashKey<DOMIntersectionObserver>> mIntersectionObservers; + RefPtr<DOMIntersectionObserver> mLazyLoadImageObserver; + // Stack of fullscreen elements. When we request fullscreen we push the // fullscreen element onto this stack, and when we cancel fullscreen we // pop one off this stack, restoring the previous fullscreen state nsTArray<nsWeakPtr> mFullscreenStack; // The root of the doc tree in which this document is in. This is only // non-null when this document is in fullscreen mode. nsWeakPtr mFullscreenRoot;
--- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -16,16 +16,17 @@ #include "nsImageFrame.h" #include "nsIScriptContext.h" #include "nsContentUtils.h" #include "nsContainerFrame.h" #include "nsNodeInfoManager.h" #include "mozilla/MouseEvents.h" #include "nsContentPolicyUtils.h" #include "nsFocusManager.h" +#include "mozilla/dom/DOMIntersectionObserver.h" #include "mozilla/dom/HTMLFormElement.h" #include "mozilla/dom/MutationEventBinding.h" #include "mozilla/dom/UserActivation.h" #include "nsAttrValueOrString.h" #include "imgLoader.h" #include "Image.h" // Responsive images! @@ -108,16 +109,17 @@ class ImageLoadTask final : public Micro bool mUseUrgentStartForChannel; }; HTMLImageElement::HTMLImageElement( already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) : nsGenericHTMLElement(std::move(aNodeInfo)), mForm(nullptr), mInDocResponsiveContent(false), + mLazyLoading(false), mCurrentDensity(1.0) { // We start out broken AddStatesSilently(NS_EVENT_STATE_BROKEN); } HTMLImageElement::~HTMLImageElement() { DestroyImageLoadingContent(); } NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLImageElement, nsGenericHTMLElement, @@ -304,16 +306,30 @@ nsresult HTMLImageElement::BeforeSetAttr nsresult HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal, bool aNotify) { nsAttrValueOrString attrVal(aValue); + if (aName == nsGkAtoms::loading && aNameSpaceID == kNameSpaceID_None) { + if (aValue && + static_cast<HTMLImageElement::Loading>(aValue->GetEnumValue()) == + Loading::Lazy && + !ImageState().HasState(NS_EVENT_STATE_LOADING)) { + SetLazyLoading(); + } else if (aOldValue && + static_cast<HTMLImageElement::Loading>( + aOldValue->GetEnumValue()) == Loading::Lazy && + !ImageState().HasState(NS_EVENT_STATE_LOADING)) { + StopLazyLoadingAndStartLoadIfNeeded(); + } + } + if (aValue) { AfterMaybeChangeAttr(aNameSpaceID, aName, attrVal, aOldValue, aMaybeScriptedPrincipal, true, aNotify); } if (aNameSpaceID == kNameSpaceID_None && mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && aValue && !aValue->IsEmptyString()) { @@ -404,17 +420,17 @@ void HTMLImageElement::AfterMaybeChangeA this, aValue.String(), aMaybeScriptedPrincipal); if (InResponsiveMode()) { if (mResponsiveSelector && mResponsiveSelector->Content() == this) { mResponsiveSelector->SetDefaultSource(aValue.String(), mSrcTriggeringPrincipal); } QueueImageLoadTask(true); - } else if (aNotify && OwnerDoc()->ShouldLoadImages()) { + } else if (aNotify && ShouldLoadImage()) { // If aNotify is false, we are coming from the parser or some such place; // we'll get bound after all the attributes have been set, so we'll do the // sync image load from BindToTree. Skip the LoadImage call in that case. // Note that this sync behavior is partially removed from the spec, bug // 1076583 // A hack to get animations to reset. See bug 594771. @@ -462,17 +478,17 @@ void HTMLImageElement::AfterMaybeChangeA // Mark channel as urgent-start before load image if the image load is // initaiated by a user interaction. mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput(); if (InResponsiveMode()) { // per spec, full selection runs when this changes, even though // it doesn't directly affect the source selection QueueImageLoadTask(true); - } else if (OwnerDoc()->ShouldLoadImages()) { + } else if (ShouldLoadImage()) { // Bug 1076583 - We still use the older synchronous algorithm in // non-responsive mode. Force a new load of the image with the // new cross origin policy ForceReload(aNotify, IgnoreErrors()); } } } @@ -553,17 +569,17 @@ nsresult HTMLImageElement::BindToTree(Bi // We still act synchronously for the non-responsive case (Bug // 1076583), but still need to delay if it is unsafe to run // script. // If loading is temporarily disabled, don't even launch MaybeLoadImage. // Otherwise MaybeLoadImage may run later when someone has reenabled // loading. - if (LoadingEnabled() && aContext.OwnerDoc().ShouldLoadImages()) { + if (LoadingEnabled() && ShouldLoadImage()) { nsContentUtils::AddScriptRunner( NewRunnableMethod<bool>("dom::HTMLImageElement::MaybeLoadImage", this, &HTMLImageElement::MaybeLoadImage, false)); } } return rv; } @@ -627,31 +643,27 @@ void HTMLImageElement::MaybeLoadImage(bo EventStates HTMLImageElement::IntrinsicState() const { return nsGenericHTMLElement::IntrinsicState() | nsImageLoadingContent::ImageState(); } void HTMLImageElement::NodeInfoChanged(Document* aOldDoc) { nsGenericHTMLElement::NodeInfoChanged(aOldDoc); + + if (mLazyLoading) { + aOldDoc->GetLazyLoadImageObserver()->Unobserve(*this); + mLazyLoading = false; + SetLazyLoading(); + } + // Force reload image if adoption steps are run. // If loading is temporarily disabled, don't even launch script runner. // Otherwise script runner may run later when someone has reenabled loading. - if (LoadingEnabled() && OwnerDoc()->ShouldLoadImages()) { - // Use script runner for the case the adopt is from appendChild. - // Bug 1076583 - We still behave synchronously in the non-responsive case - nsContentUtils::AddScriptRunner( - (InResponsiveMode()) - ? NewRunnableMethod<bool>( - "dom::HTMLImageElement::QueueImageLoadTask", this, - &HTMLImageElement::QueueImageLoadTask, true) - : NewRunnableMethod<bool>("dom::HTMLImageElement::MaybeLoadImage", - this, &HTMLImageElement::MaybeLoadImage, - true)); - } + StartLoadingIfNeeded(); } // static already_AddRefed<HTMLImageElement> HTMLImageElement::Image( const GlobalObject& aGlobal, const Optional<uint32_t>& aWidth, const Optional<uint32_t>& aHeight, ErrorResult& aError) { nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports()); Document* doc; @@ -729,17 +741,17 @@ nsresult HTMLImageElement::CopyInnerTo(H if (!destIsStatic) { // In SetAttr (called from nsGenericHTMLElement::CopyInnerTo), aDest skipped // doing the image load because we passed in false for aNotify. But we // really do want it to do the load, so set it up to happen once the cloning // reaches a stable state. if (!aDest->InResponsiveMode() && aDest->HasAttr(kNameSpaceID_None, nsGkAtoms::src) && - aDest->OwnerDoc()->ShouldLoadImages()) { + aDest->ShouldLoadImage()) { // Mark channel as urgent-start before load image if the image load is // initaiated by a user interaction. mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput(); nsContentUtils::AddScriptRunner(NewRunnableMethod<bool>( "dom::HTMLImageElement::MaybeLoadImage", aDest, &HTMLImageElement::MaybeLoadImage, false)); } @@ -795,17 +807,17 @@ void HTMLImageElement::ClearForm(bool aR UnsetFlags(ADDED_TO_FORM); mForm = nullptr; } void HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad) { // If loading is temporarily disabled, we don't want to queue tasks // that may then run when loading is re-enabled. - if (!LoadingEnabled() || !OwnerDoc()->ShouldLoadImages()) { + if (!LoadingEnabled() || !ShouldLoadImage()) { return; } // Ensure that we don't overwrite a previous load request that requires // a complete load to occur. bool alwaysLoad = aAlwaysLoad; if (mPendingImageLoadTask) { alwaysLoad = alwaysLoad || mPendingImageLoadTask->AlwaysLoad(); @@ -1224,10 +1236,65 @@ void HTMLImageElement::DestroyContent() nsGenericHTMLElement::DestroyContent(); } void HTMLImageElement::MediaFeatureValuesChanged() { QueueImageLoadTask(false); } +bool HTMLImageElement::ShouldLoadImage() const { + return OwnerDoc()->ShouldLoadImages() && !mLazyLoading; +} + +void HTMLImageElement::SetLazyLoading() { + if (mLazyLoading) { + return; + } + + // If scripting is disabled don't do lazy load. + // https://whatpr.org/html/3752/images.html#updating-the-image-data + if (!OwnerDoc()->IsScriptEnabled()) { + return; + } + + // There (maybe) is a race condition that we have no LazyLoadImageObserver + // when the root document has been removed from the docshell. + // In the case we don't need to worry about lazy-loading. + if (DOMIntersectionObserver* lazyLoadObserver = + OwnerDoc()->GetLazyLoadImageObserver()) { + lazyLoadObserver->Observe(*this); + mLazyLoading = true; + } +} + +void HTMLImageElement::StartLoadingIfNeeded() { + if (LoadingEnabled() && ShouldLoadImage()) { + // Use script runner for the case the adopt is from appendChild. + // Bug 1076583 - We still behave synchronously in the non-responsive case + nsContentUtils::AddScriptRunner( + (InResponsiveMode()) + ? NewRunnableMethod<bool>( + "dom::HTMLImageElement::QueueImageLoadTask", this, + &HTMLImageElement::QueueImageLoadTask, true) + : NewRunnableMethod<bool>("dom::HTMLImageElement::MaybeLoadImage", + this, &HTMLImageElement::MaybeLoadImage, + true)); + } +} + +void HTMLImageElement::StopLazyLoadingAndStartLoadIfNeeded() { + if (!mLazyLoading) { + return; + } + mLazyLoading = false; + + DOMIntersectionObserver* lazyLoadObserver = + OwnerDoc()->GetLazyLoadImageObserver(); + MOZ_ASSERT(lazyLoadObserver); + + lazyLoadObserver->Unobserve(*this); + + StartLoadingIfNeeded(); +} + } // namespace dom } // namespace mozilla
--- a/dom/html/HTMLImageElement.h +++ b/dom/html/HTMLImageElement.h @@ -254,16 +254,18 @@ class HTMLImageElement final : public ns * further <source> or <img> tags would be considered. */ static bool SelectSourceForTagWithAttrs( Document* aDocument, bool aIsSourceTag, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr, const nsAString& aSizesAttr, const nsAString& aTypeAttr, const nsAString& aMediaAttr, nsAString& aResult); + void StopLazyLoadingAndStartLoadIfNeeded(); + protected: virtual ~HTMLImageElement(); // Queues a task to run LoadSelectedImage pending stable state. // // Pending Bug 1076583 this is only used by the responsive image // algorithm (InResponsiveMode()) -- synchronous actions when just // using img.src will bypass this, and update source and kick off @@ -374,17 +376,28 @@ class HTMLImageElement final : public ns * @param aNotify Whether we plan to notify document observers. */ void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue, const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal, bool aValueMaybeChanged, bool aNotify); + bool ShouldLoadImage() const; + + // Set this image as a lazy load image due to loading="lazy". + void SetLazyLoading(); + + void StartLoadingIfNeeded(); + bool mInDocResponsiveContent; + + // Represents the image is deferred loading until this element gets visible. + bool mLazyLoading; + RefPtr<ImageLoadTask> mPendingImageLoadTask; nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal; nsCOMPtr<nsIPrincipal> mSrcsetTriggeringPrincipal; // Last URL that was attempted to load by this element. nsCOMPtr<nsIURI> mLastSelectedSource; // Last pixel density that was selected. double mCurrentDensity;
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/below-viewport-image-loading-lazy-load-event.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[below-viewport-image-loading-lazy-load-event.tentative.html] - [Below-viewport loading=lazy images do not block the window load event when scrolled into viewport] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/disconnected-image-loading-lazy.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[disconnected-image-loading-lazy.tentative.html] - [loading=lazy for disconnected image] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/image-loading-lazy-load-event.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[image-loading-lazy-load-event.tentative.html] - [In-viewport loading=lazy images do not block the window load event] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/image-loading-lazy.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[image-loading-lazy.tentative.html] - [Images with loading='lazy' load only when in the viewport] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/move-element-and-scroll.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[move-element-and-scroll.tentative.html] - [Test that <img> below viewport is not loaded when moved to another document and then scrolled to] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/not-rendered-below-viewport-image-loading-lazy.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[not-rendered-below-viewport-image-loading-lazy.tentative.html] - [Below-viewport loading=lazy not-rendered images should never load, even when scrolled into view] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/not-rendered-image-loading-lazy.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[not-rendered-image-loading-lazy.tentative.html] - [In-viewport loading=lazy not-rendered images should never load] - expected: FAIL -
--- a/testing/web-platform/meta/loading/lazyload/original-base-url-applied-2-tentative.html.ini +++ b/testing/web-platform/meta/loading/lazyload/original-base-url-applied-2-tentative.html.ini @@ -1,4 +1,5 @@ [original-base-url-applied-2-tentative.html] [Deferred images with loading='lazy' use the original base URL specified at the parse time] expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1613277
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/loading/lazyload/original-base-url-applied-tentative.html.ini @@ -0,0 +1,5 @@ +[original-base-url-applied-tentative.html] + [Test that when deferred img is loaded, it uses the base URL computed at parse time.] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1613277 +
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/loading/lazyload/original-crossorigin-applied-tentative.sub.html.ini @@ -0,0 +1,5 @@ +[original-crossorigin-applied-tentative.sub.html] + [Test that when deferred image is loaded, it uses the crossorigin attribute specified at parse time.] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1613277 +
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/loading/lazyload/original-referrer-policy-applied-tentative.sub.html.ini @@ -0,0 +1,5 @@ +[original-referrer-policy-applied-tentative.sub.html] + [Test that when deferred img is loaded, it uses the referrer-policy specified at parse time.] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1613277 +
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/picture-loading-lazy.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[picture-loading-lazy.tentative.html] - [Test that the loading=lazy <picture> element below viewport was deferred, on document load.] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/loading/lazyload/remove-element-and-scroll.tentative.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[remove-element-and-scroll.tentative.html] - [Test that <img> below viewport is not loaded when removed from the document and then scrolled to] - expected: FAIL -
new file mode 100644 --- /dev/null +++ b/testing/web-platform/tests/loading/lazyload/image-loading-lazy-move-document.tentative.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<head> +<title>Moving loading='lazy' image into another top level document</title> +<link rel="help" href="https://github.com/scott-little/lazyload"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> + +<!-- +Marked as tentative until https://github.com/whatwg/html/pull/3752 is landed. +--> + +<div style="height:1000vh;"></div> +<img loading="lazy" + src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC"> +<script> +promise_test(async t => { + let image_loaded = false; + const img = document.querySelector("img"); + img.addEventListener("load", () => { image_loaded = true; }); + + await new Promise(resolve => window.addEventListener("load", resolve)); + + assert_false(image_loaded, + "lazy-load image shouldn't be loaded yet"); + + const anotherWin = window.open("resources/newwindow.html"); + + await new Promise(resolve => anotherWin.addEventListener("load", resolve)); + + anotherWin.document.body.appendChild(img); + + assert_false(image_loaded, + "lazy-load image shouldn't be loaded yet"); + + img.scrollIntoView(); + + await new Promise(resolve => img.addEventListener("load", resolve)); + assert_true(img.complete, + "Now the lazy-load image should be loaded"); +}); +</script>