WordPress.org

Make WordPress Core

Opened 6 months ago

Closed 6 months ago

Last modified 6 months ago

#52768 closed defect (bug) (fixed)

WordPress post URL oEmbed rendering blocked by iframe lazy-loading

Reported by: SirStuey Owned by: peterwilsoncc
Milestone: 5.7.1 Priority: normal
Severity: normal Version: 5.7
Component: Embeds Keywords: needs-testing has-patch has-unit-tests fixed-major
Focuses: performance Cc:

Description

After updating to 5.7, WordPress post URL oEmbeds are not rendering properly, displaying blockquote text fallback links instead.

Behavior was replicated on test server with no plugins and default Twenty Twenty-One theme.

Disabling iframe lazy-loading (source) remedies the issue and results in expected oEmbed post display behavior.

Windows 10
Chrome & Edge: WP post URL oEmbed blocked unless iframe lazy-loading disabled
Firefox: unaffected

macOS Catalina
Chrome: WP post URL oEmbed blocked unless iframe lazy-loading disabled
Safari: unaffected

iOS 14.4 Chrome & Safari: unaffected

All browsers are latest versions.

With default WP 5.8 treatment, ' loading="lazy" ' is appended to the WP post oEmbed iframe opening tag, and although the iframes appear in the page source code they do not appear on the rendered page. This seems to be tied to desktop browsers (Chrome and Edge on Windows, Chrome on macOS).

This only seems to affect WordPress post URL oEmbed, as other sources of oEmbed links display correctly, such as YouTube and Instagram.

Attachments (2)

WP URL Embed Blocked.JPG (100.3 KB) - added by SirStuey 6 months ago.
WP URL Embed Proper Display.JPG (91.5 KB) - added by SirStuey 6 months ago.

Download all attachments as: .zip

Change History (35)

#1 @swissspidy
6 months ago

  • Component changed from General to Embeds
  • Focuses performance added

#2 follow-up: @adamsilverstein
6 months ago

Thanks for raising this @SirStuey.

While we are looking into this, you can disable lazy-loading for these posts if you need to using the approach outlined in the post about the feature - https://make.wordpress.org/core/2021/02/19/lazy-loading-iframes-in-5-7/.

cc: @flixos90 who did the iframe lazy loading implementation.

#3 @johnbillion
6 months ago

  • Keywords needs-testing added
  • Milestone changed from Awaiting Review to 5.7.1

Moving to 5.7.1 for visibility

#4 in reply to: ↑ 2 @SirStuey
6 months ago

@adamsilverstein

Thank you! I ruled out any customization or server-specific influences, and the issue seems connected to how different browsers treat the WordPress URL embed with iframe lazy loading activated.

Attached are screenshots of the Wordpress 5.7 Field Guide showing how it displays differently in Windows 10 chrome and firefox browsers. Chrome does support iframe lazy loading, but something is preventing the iframe from ever loading.

I used Felix Arntz' customization functions to disable the iframe lazy-loading for testing, and that's where it became clear 5.7's iframe lazy loading default was the affecting change.

I'm using the second function now, to disable iframe lazy loading for my own Wordpress post URL embeds.

@flixos90 - thank you for providing those customization functions! (The second function example 'skip_loading_lazy_youtube_iframes' is missing a closing parenthesis at the end of the if statement on line 2. I'm sorry if this is the wrong place to mention this, I didn't want to leave a public comment on the post itself and couldn't find a way to message you privately.)

#5 @fabianpimminger
6 months ago

I can replicate this issue at my sites with Chrome on macOS. After disabling the lazy loading with the mentioned filter, the issue dissapears and all post embeds display correctly in Chrome.

#6 @peterwilsoncc
6 months ago

I've tested this in macOS Chrome and Chrome Canary embedding on a site in both HTTP and HTTPS and am able to reproduce it with WordPress embeds (I used wordpress.org and make.wordpress.org posts).

The source code for a WP embed is using some fairly strong security controls (it's basically how WP knows to trust an unapproved oembed endpoint) so I am wondering if that is having an effect.

There is also some JavaScript messaging that gets sent around that may not be firing correctly for iframes that haven't yet loaded.

Version 0, edited 6 months ago by peterwilsoncc (next)

#7 follow-ups: @flixos90
6 months ago

@SirStuey @peterwilsoncc I debugged this for a while, but haven't been able to come to a proper conclusion on why exactly this is happening. Here are some of my observations:

  • When I visit https://make.wordpress.org/core/2021/02/23/wordpress-5-7-field-guide/ on Chrome and Mac OS (Catalina), I see the embeds working as expected. So the particular URL used as example here works for me, so even this seems to be more specific than a certain browser on a certain OS.
  • When I embed a WordPress post on one of my test sites, I can replicate the error with the same browser and OS, so I'm not sure why it's failing in one place and not the other.
  • It looks like for whatever reason in certain situations the iframe is not loaded by the browser because it is visually hidden by default (using position: absolute; clip: rect(1px, 1px, 1px, 1px);); that makes sense to me because it thinks that "this iframe isn't visible, so it doesn't need to be loaded" (without loading="lazy" the iframe of course gets loaded regardless of visibility since all iframes would load immediately).
  • Because the iframe isn't loaded, it doesn't fire the height postMessage event (see wp-embed-template.js) which is why the iframe isn't made visible while hiding the fallback link (see wp-embed.js).
  • My biggest confusion is the question what situation exactly causes this here (which browser, maybe which browser version? which OS? something else? etc).

Potentially to fix this in 5.7.1 we should simply not add the loading attribute to WordPress embed iframes. We could do that by simply returning early in wp_iframe_tag_add_loading_attr() if the current iframe is for a WordPress embed (e.g. check existence of wp-embedded-content class). We could then further investigate this problem and potentially fix it really in a follow-up issue.

What do you think?

#8 in reply to: ↑ 7 @peterwilsoncc
6 months ago

Replying to flixos90:

@SirStuey @peterwilsoncc I debugged this for a while, but haven't been able to come to a proper conclusion on why exactly this is happening. Here are some of my observations:

@flixos90 I'm seeing it when using the latest versions of Chrome & Chrome Canary (no extensions in either) on macOS Big Sur (11.2.1).

...

Potentially to fix this in 5.7.1 we should simply not add the loading attribute to WordPress embed iframes. We could do that by simply returning early in wp_iframe_tag_add_loading_attr() if the current iframe is for a WordPress embed (e.g. check existence of wp-embedded-content class). We could then further investigate this problem and potentially fix it really in a follow-up issue.

What do you think?

I'm waiting to hear back from the reporter but #52856 suggests it is affecting Amazon embeds too so I am a little concerned it's a wider issue. I'm undecided as to whether this a browser or WP implementation bug but, either way, given Chrome's market dominance probably something WordPress ought to fix/work-around.

I'm a little worried such a targeted fix would miss other affected embeds, either WP native or added via raw HTML by a user with the unfiltered_html capability.

#9 @SirStuey
6 months ago

@flixos90 

I have not searched widely, but it seems that blocking lazy-loading for WordPress embeds would do the trick until the different browser treatments are better understood.

There are others experiencing the same:

https://wordpress.org/support/topic/embed-blocks-not-working-in-chrome-ms-edge/
https://wordpress.org/support/topic/link-embed-blocks-not-working-in-chrome/

It's also possible there could be plugin conflicts, but developers have been issuing updates as needed, e.g.:

https://wordpress.org/support/topic/maybe-conflict-with-wordpress-5-7-in-wp-embed/

@peterwilsoncc

I haven't seen issues with other embeds, such as social media or YouTube links. Amazon links are tricky, because I don't see proper Amazon embeds with 5.7 OR previous WordPress versions. With Amazon links, I see a Kindle e-reader preview attempt in my own domains, local test server, and the linked page included in ticket #52856, across different browsers and devices.

Since Woo Commerce embeds are powered by Wordpress, they'll be subject to the same browser treatment described here.

One potential question to ask is whether the behavior is related to different browser versions, or if it's based on device type. I haven't been able to test this on an older desktop browser.

Chrome iOS Version 87.0.4280.77 (latest) : NO embed issue
Chrome Windows Version 89.0.4389.90 (latest): Embed issue is present

#10 @peterwilsoncc
6 months ago

#52899 was marked as a duplicate.

#11 @SirStuey
6 months ago

I've been trying to dig up more about how Chrome treats iframe lazy-loading.

https://web.dev/iframe-lazy-loading/

The loading attribute affects iframes differently than images, depending on whether the iframe is hidden. (Hidden iframes are often used for analytics or communication purposes.) Chrome uses the following criteria to determine whether an iframe is hidden:

  • The iframe's width and height are 4px or smaller.
  • display: none or visibility: hidden is applied.
  • The iframe is placed off-screen using negative X or Y positioning.
  • This criteria applies to both loading=lazy and loading=auto

If an iframe meets any of these conditions, Chrome considers it hidden and won't lazy-load it in most cases.

That first clause is the culprit, caused by clip: rect(1px, 1px, 1px, 1px) leads Chrome (and Edge) to treat the iframe as intentionally hidden.

clip is also deprecated, with clip-path the preferred replacement.

I tested things locally via embed.php, replacing clip: rect(1px, 1px, 1px, 1px) with various forms of clip-path.

It's my understanding that clip: rect(1px, 1px, 1px, 1px) has no height or width given that all of the dimensions are with respect to the top left corner, and so I tried to approximate the same with clip-path inset values.

clip-path: inset(0 100% 100% 0); clips the image from the right and bottom, resulting in 0 pixel width located at the top left of the element.

This does not improve the situation, the issue is present and the iframe is not displayed. I thought the issue was due to clip being deprecated, but mimicking the same clipping behavior with clip-path causes the same iframe rendering issue.

However, if the clipping path is modified. e.g. clip-path: inset(0 50% 50% 0); , the browser produces the desired iframe display behavior. I tested this on desktop (Windows 10) and for Chrome. It does not seem to negatively impact other embed sources (e.g. YouTube), and I see the blockquote link fallback on page-load before it's quickly replaced with the iframe as expected. I have not tested on mobile, and I'm not certain what undesirable behaviors to look for.

The problem is, if the iframe is hidden in most conventional ways, Chrome will treat it as hidden.

It's unclear to me why different browsers or browser versions treat this differently, such as why is the hidden iframe is treated differently in Chrome for desktops vs. Chrome of mobile. But, unless the browser treatment changes, lazy loading conflicts with how the WordPress embeds are hidden by default.

This ticket was mentioned in Slack in #core by audrasjb. View the logs.


6 months ago

#13 @peterwilsoncc
6 months ago

I say this reluctantly, but I am wondering if the iframe lazy loading ought to be reverted while a solution is figured out. While it's possible to change the WP embed code to remove the clipping, the problem will remain for previous embeds.

As the postMessage is fired within the WP embed iframes, if they can be fixed in lazy loading then they should still fire.

With RC1 due on April 7, I suggest fix or decision to revert be made on April 1 -- the Thursday prior to the Good Friday holiday in much of the world.

#14 in reply to: ↑ 7 ; follow-up: @SirStuey
6 months ago

Replying to peterwilsoncc:

I say this reluctantly, but I am wondering if the iframe lazy loading ought to be reverted while a solution is figured out.

Have there been any other bug reports or wordpress.org support requests regarding embeds other than those related to WordPress post (or product) embeds?

If the iframe lazy loading update is reverted, would there be a timeline for its reimplementation? Even if the WordPress embed conflict is not resolve, it is still beneficial to have built-in lazy loading of other types of embeds, such as YouTube links.

There's not a lot of speed to be gained by lazy-loading Wordpress embeds, but there is a lot to be lost by not lazy-loading embedded YouTube videos.

Respectfully, speaking personally, I'd prefer to keep lazy-loading. It seems to work well, and Felix's customization script has everything on my site displaying properly until either a patch or reversion is settled on. But, I also typically only embed my own Wordpress posts, or URLs from popular social media or video hosting platforms. I'm not fully versed with how others might embed Wordpress posts.

Lazy-loading YouTube iframes was already on my to-do list ahead of Google's May speed-based algorithm changes.

peterwilsoncc:

I'm a little worried such a targeted fix would miss other affected embeds, either WP native or added via raw HTML by a user with the unfiltered_html capability.

If there exists the strong potential for unanticipated behaviors, would it be practical to instead designate a limited list of included URL embed iframes to be given lazy loading?

(Thank you both very much for your attention to this!)

#15 in reply to: ↑ 14 ; follow-up: @peterwilsoncc
6 months ago

Replying to SirStuey:

Have there been any other bug reports or wordpress.org support requests regarding embeds other than those related to WordPress post (or product) embeds?

There is #52856 but I am still waiting to hear back as to whether it is related to lazy loading.

I agree with your assessment that a full revert would be a step backwards. I mention it as a last resort to get the full range of options on the ticket.

But, I also typically only embed my own WordPress posts, or URLs from popular social media or video hosting platforms. I'm not fully versed with how others might embed WordPress posts.

Lazy-loading YouTube iframes was already on my to-do list ahead of Google's May speed-based algorithm changes.

This raises an excellent point, embed providers that are known to work well with lazy loading could be detected and lazy loaded. An allow list of such providers (YouTube, Twitter, etc) would cover most embeds without the need for a full revert.

Thanks for the push back and allowing the range of options available for a fix to increase. 🙂

#16 in reply to: ↑ 15 @SirStuey
6 months ago

Replying to peterwilsoncc:

Replying to SirStuey:

Have there been any other bug reports or wordpress.org support requests regarding embeds other than those related to WordPress post (or product) embeds?

There is #52856 but I am still waiting to hear back as to whether it is related to lazy loading.

I would suspect that since WooCommerce is a WordPress plugin, the embeds are treated in the same manner as Wordpress posts, with some browsers treating them as hidden by intent due to the default embed behavior.

Amazon embeds look the same to me - improper Kindle "previews" instead of product snippets - before and after the 5.7 update.

I would assume that WooCommerce embeds will display properly if identified and excluded as Wordpress embeds.

This raises an excellent point, embed providers that are known to work well with lazy loading could be detected and lazy loaded. An allow list of such providers (YouTube, Twitter, etc) would cover most embeds without the need for a full revert.

Thanks for the push back and allowing the range of options available for a fix to increase. 🙂

Thank you, I'm glad for the opportunity to be heard and also to help in any way!

#17 @flixos90
6 months ago

  • Owner set to flixos90
  • Status changed from new to assigned

I believe the underlying issue here is related to the iframe being visually hidden, and that's the reason the browser's lazy-loading implementation may interpret it as irrelevant for being loaded. Then, because it is not loaded, it does not send the postMessage to the embedding page which would be required for the wp-embed.js logic that makes it visible to kick in.

I suggest the following: Until we can come up with an actual solution for WordPress embeds (and any other embeds that rely on being hidden in combination with wp-embed.js to work), we disable lazy-loading for those. This can be accomplished by not adding the loading attribute to the iframe if it has a data-secret attribute (which is used to associate the iframe with fallback content and then hide it initially).

This ticket was mentioned in PR #1133 on WordPress/wordpress-develop by felixarntz.


6 months ago

  • Keywords has-patch has-unit-tests added

wp_filter_oembed_result() checks for any "unknown" oEmbed provider whether there is a fallback blockquote element present for the iframe to load. If so, it hides the iframe visually and adds a generated data-secret attribute to the iframe and blockquote to associate them with each other. Then, the wp-embed.js script goes through the page looking for such elements. Once the iframe has been loaded (and thus notified the wp-embed.js script via postMessage), the script makes the iframe visible and hides the blockquote.

This mechanism can break when the iframe is marked to be lazy-loaded: Since the browser may interpret the hidden iframe as being irrelevant (since it's hidden), it will never load it, hence the iframe will not send the postMessage event to become visible.

This PR ensures that such iframes do not receive the loading attribute and are instead kept unmodified. It also fixes a minor issue so that the wp_img_tag_add_loading_attr and wp_iframe_tag_add_loading_attr filters no longer fire unnecessarily, and it fixes a pointless iframe test from before.

Trac ticket: https://core.trac.wordpress.org/ticket/52768

#19 @flixos90
6 months ago

  • Owner changed from flixos90 to peterwilsoncc
  • Status changed from assigned to reviewing

I've added a PR with a fix - please let me know your thoughts.

This ticket was mentioned in Slack in #forums by ntsekouras. View the logs.


6 months ago

#21 @joyously
6 months ago

I would like to understand why it needs to be hidden in the first place. It could even help a user to understand what the embed is that eventually appears, if the URL is shown first.
I vote for the lesser of two evils: show the link and keep the lazy load, rather than hide the link and remove the lazy load.

This ticket was mentioned in Slack in #core by joyously. View the logs.


6 months ago

#23 @jonkastonka
6 months ago

Adding this to the theme seems to squash the bug:

.wp-embedded-content {
	position: static !important;
}

So just removing position: absolute would probably fix it. And I agree with @joyously, I don't understand why this would need to be hidden or absolute positioned in the first place.

#24 @SirStuey
6 months ago

@joyously - the link is shown first, in the fallback blockquote.

Why are the iframes hidden by default? I am guessing that it's because the fallback loads by default. The fallback link is displayed and then replaced by the iframe. Hiding the iframe ensures you get one or the other, and not both.

Let's say that a WordPress URL is embedded in a post. That referenced link might change in time, either with a new URL, or it might be removed entirely. I've observed that, given the current way the embed is handled, the fallback URL will be displayed in blockquotes. If the target URL has changed or is removed, the iframe does not redirect, the fallback is displayed.

When that fallback link is displayed, the iframe is hidden. If that iframe is able to load with content from the embed source, it loads visibly and the fallback is hidden.

If there's nothing in that iframe, what's there to load? If the iframe isn't to be hidden by default, then that fallback might be redundant and you can have unpredictable display behavior.

If the URL has changed and is properly redirected, clicking the blockquote link will still get you to the proper destination.

There could be other reasons the fallback behavior was created.

The question is this - can this WP embed hidden iframe behavior be reworked or removed, while still ensuring proper fallback link display when needed, and while keeping in mind any security concerns due to WP embeds coming in from potentially anywhere? How long would this take, and who might it involve, compared to the fix that the iframe lazy loading developer already came up with?

The proposed fix should perfectly remedy things for the time being.

With that update in place, the embed behavior can then be potentially reworked in a way that allows for iframe lazy loading. But, that's also a much bigger task since the developers will have to think about existing and future iframes. It also cannot be a theme-based css fix.

There are 3 main approaches to fixing this bug.

1) Revert the iframe lazy loading that was implemented in 5.7
2) Update the 5.7 update to exclude Wordpress URL embed iframes from lazy loading
3) Rework Wordpress URL embeds

Option 2) is the fastest remedy for this issue, and it should not affect iframes unrelated to this issue, such as YouTube embeds.

Option 3) should be the next step if lazy loading Wordpress URL embeds is still a priority. This would have also have to take into account both future and existing embeds.

#25 @SergeyBiryukov
6 months ago

#52962 was marked as a duplicate.

#26 @peterwilsoncc
6 months ago

I've added a comment to PR #1133 with a minor issue around single quoted attributes.

@flixos90 if you want to consider that as a separate issue then I am happy if a follow up ticket is created for the purpose. I don't hold a firm view either way.

#27 follow-up: @flixos90
6 months ago

@peterwilsoncc Just replied to the PR feedback - I agree a separate follow-up ticket makes sense here.

#28 in reply to: ↑ 27 @peterwilsoncc
6 months ago

Replying to flixos90:

... I agree a separate follow-up ticket makes sense here.

Ticket #52990 created for this purpose.

#29 @peterwilsoncc
6 months ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 50682:

Media: Do not lazy load hidden images or embeds.

Improve the check for sourceless or dimensionless media when determining if the lazy loading attribute should be added to iframes and images. Never include the lazy loading attribute on embeds of WordPress posts as the iframe is initially hidden.

Including loading="lazy" on initially hidden iframes and images can prevent the media from loading in some browsers.

Props adamsilverstein, fabianpimminger, flixos90, johnbillion, jonkastonka, joyously, peterwilsoncc, SergeyBiryukov, SirStuey, swissspidy.
Fixes #52768.

#30 @peterwilsoncc
6 months ago

In 50683:

Build tools: Revert package-lock.json change in [50682].

See #52768.

#31 @peterwilsoncc
6 months ago

  • Keywords fixed-major added
  • Resolution fixed deleted
  • Status changed from closed to reopened

Reopening for merge to the 5.7 branch

#32 @peterwilsoncc
6 months ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 50684:

Media: Do not lazy load hidden images or embeds.

Improve the check for sourceless or dimensionless media when determining if the lazy loading attribute should be added to iframes and images. Never include the lazy loading attribute on embeds of WordPress posts as the iframe is initially hidden.

Including loading="lazy" on initially hidden iframes and images can prevent the media from loading in some browsers.

Props adamsilverstein, fabianpimminger, flixos90, johnbillion, jonkastonka, joyously, peterwilsoncc, SergeyBiryukov, SirStuey, swissspidy.
Merges [50682], [50683] to the 5.7 branch.
Fixes #52768.

Note: See TracTickets for help on using tickets.