Open Bug 1926802 Opened 1 month ago Updated 2 days ago

loading lazy doesn't work with template cloning ( cloneNode or importNode )

Categories

(Core :: DOM: Core & HTML, defect)

Firefox 131
defect

Tracking

()

People

(Reporter: tito.bouzout, Unassigned)

Details

(Keywords: parity-chrome)

Attachments

(1 obsolete file)

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36

Steps to reproduce:

Loading set to lazy doesn't work when a template is cloned and appended to the document. At least, I couldn't figure out a way to make it work.

const tpl = document.createElement("template");
tpl.innerHTML = `<div style="height: 2500px">
      <style>html, body{ height: 2500px}</style>
      <div style="height: 2500px">Spacer</div>
      <br/>
      <img
        loading="lazy"
        src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png?${Date.now()}"
        height="108"
        width="320"
        style="height: 108px;width:320px;"
        onload="console.log('image loaded')"
      > 
    </div>`;

const useCloneNode = false;

const img = useCloneNode ? tpl.cloneNode(true) : document.importNode(tpl, true);

setTimeout(() => {
  document.body.append(img.content);
}, 2000);

Actual results:

The image is fetched from the server as soon as the template is appended.

Expected results:

The image should load when in near the viewport.

The Bugbug bot thinks this bug should belong to the 'Core::DOM: Core & HTML' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.

Component: Untriaged → DOM: Core & HTML
Product: Firefox → Core

testcases:

Hiro, could you take a look?

Severity: -- → S3
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: needinfo?(hikezoe.birchill)
Keywords: parity-chrome

The problem here in HTMLImageElement::SetLazyLoading;

  // If scripting is disabled don't do lazy load.                               
  // https://whatpr.org/html/3752/images.html#updating-the-image-data           
  //                                                                            
  // Same for printing.                                                         
  Document* doc = OwnerDoc();                                                   
  if (!doc->IsScriptEnabled() || doc->IsStaticDocument()) {                     
    return;                                                                     
  } 

Unfortunately at that time, when the template's innerHTML is set, the !doc->IsScriptEnabled() is true. That's the cause. We need to tweak the condition.

I'd defer it to DOM team.

Flags: needinfo?(hikezoe.birchill)

Thank you for the investigation. I'll ask who can work on this.

(In reply to Hiroyuki Ikezoe (:hiro) from comment #3)

The problem here in HTMLImageElement::SetLazyLoading;

  // If scripting is disabled don't do lazy load.                               
  // https://whatpr.org/html/3752/images.html#updating-the-image-data           
  //                                                                            
  // Same for printing.                                                         
  Document* doc = OwnerDoc();                                                   
  if (!doc->IsScriptEnabled() || doc->IsStaticDocument()) {                     
    return;                                                                     
  } 

Unfortunately at that time, when the template's innerHTML is set, the !doc->IsScriptEnabled() is true. That's the cause. We need to tweak the condition.

I'd defer it to DOM team.

Hi Henri, do you know where we can tweak the condition for the template's innerHTML case? Thanks.

Flags: needinfo?(hsivonen)
Assignee: nobody → longsonr
Status: NEW → ASSIGNED
Assignee: longsonr → nobody
Status: ASSIGNED → NEW

(In reply to Hiroyuki Ikezoe (:hiro) from comment #3)

Unfortunately at that time, when the template's innerHTML is set, the !doc->IsScriptEnabled() is true. That's the cause. We need to tweak the condition.

That's because the script enabled check is done with the owner document of template contents instead of the owner document of the template itself.

Perhaps documents should have a tri-state enum for scripting state: default: compute when IsScriptEnabled() is called. force-true, make IsScriptEnabled() return true. force-false, make IsScriptEnabled() return false. Then the document holding the template contents should get this set to force-true or force-false depending on what the document that owns the template element returned when the template was created.

zcorpan, does that seem reasonable? (Though we should probably find out what Chrome does.)

Flags: needinfo?(hsivonen) → needinfo?(zcorpan)
Keywords: spec-needed

Chrome and Safari lazy-loads the image in the demo in the issue description: https://software.hixie.ch/utilities/js/live-dom-viewer/saved/13284

I think we shouldn't force scripting enabled on template contents documents. It's intentional that scripting is disabled there (because the document has no browsing context).

The spec handles this as intended, as far as I can tell. Maybe there's a bug with what happens when the img element is cloned or imported, or maybe image loading checks "will lazy load element steps" to soon?

As long as the img element's node document is the template contents document, update the image data shouldn't start (or if it starts, it will be stuck in step 1 forever). With cloneNode(), the node document is the the same as the clonee until the clone is appended to body. With importNode() the node document is set to "this" (the document in document.importNode()), which causes update the image data to run at that point.

In either case, when the update the image data step 25 calls will lazy load element steps, the img element's node document should have scripting enabled (it's not the template contents document at this point).

Flags: needinfo?(zcorpan)
Keywords: spec-needed

Thank you Simon.

So the real underlying problem is probably here in HTMLImageElement::NodeInfoChanged;

    if (mLazyLoading) {
      aOldDoc->GetLazyLoadObserver()->Unobserve(*this);
      mLazyLoading = false;
      SetLazyLoading();
    }

The SetLazyLoading() call should be outside of the if (mLazyLoading) block, I mean, the function needs to be evaluated whatever the current mLazyLoading is, I guess.

Attachment #9433319 - Attachment is obsolete: true
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: