Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First pass at IntersectionObserver in Resources #26236

Merged
merged 50 commits into from
Mar 9, 2020

Conversation

dreamofabear
Copy link

@dreamofabear dreamofabear commented Jan 7, 2020

Partial for #25428.

Guarded by "intersect-resources" experiment flag. IntersectionObserver spec behavior change slated to ship in Chrome 81 in March.

Still a fair amount of TODOs to resolve e.g. dynamic loading rectangle during prerender mode, skipping intersections when scroll velocity is very high.

However, I'd like to start scaling the testing beyond myself (see below). Cost is a minor bundle size increase:

dist/amp4ads-v0.js: Δ +0.03KB
dist/shadow-v0.js: Δ +0.59KB
dist/v0.js: Δ +0.46KB

Summary

  1. IntersectionObserver callback intersects_() replaces most of discoverWork_(), with some exceptions (e.g. idleRenderOutsideViewport is unimplemented).
  2. Use IntersectionObserverEntry.boundingClientRect instead of Element.getBoundingClientRect() where possible.
  3. Disable layout hacks needed for positional offsets in elements like remeasurePass() and setRelayoutTop().

Testing

Did some manual testing across several components and behaviors. Simple performance tests look roughly comparable.

Next, will write a test plan for @diantekyrie for broader extension coverage.

Copy link
Contributor

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The possibility of stale size boxes worries me.

* @param {!LayoutRectDef} r1
* @param {!LayoutRectDef} r2
* @param {{top: number, bottom: number, left: number, right: number}} r1
* @param {{top: number, bottom: number, left: number, right: number}} r2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, I agree that there's a risk of obfuscation. layoutRectFromDomRect was made for this.

src/service/mutator-impl.js Show resolved Hide resolved
src/service/resources-impl.js Show resolved Hide resolved
src/service/resources-impl.js Outdated Show resolved Hide resolved

// Force all intersecting, non-zero-sized, non-owned elements to be built.
// E.g. ensures that all in-viewport elements are built in prerender mode.
if (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this from?

if (
!r.isBuilt() &&
!r.hasOwner() &&
r.hasBeenMeasured() &&
r.isDisplayed() &&
r.overlaps(loadRect)
) {
this.buildOrScheduleBuildForResource_(
r,
/* checkForDupes */ true,
/* scheduleWhenBuilt */ undefined,
/* force */ true
);
}

!r.isBuilt() &&
!r.isBuilding() &&
isIntersecting &&
r.isDisplayed(clientRect) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary with isIntersecting?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, it's kinda confusing. isDisplayed refers to size of the element. You can have an intersecting element with zero size.


const toUnload = [];

const promises = entries.map(entry => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there may be later entries with more up-to-date info on the same element. We may need to work from right to left, and skip the leftmost entires that are old.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this actually happen in practice? I'd be surprised.

const {boundingClientRect, isIntersecting, target, rootBounds} = entry;
// Closure Compiler doesn't recognize boundingClientRect as a ClientRect.
const clientRect = /** @type {!ClientRect} */ (boundingClientRect);
devAssert(target.isUpgraded());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a safe assertion?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, we only start observing on upgrade.

const wasIntersecting = r.isInViewport();
let isDisplayed = this.measureResource_(r, clientRect);

if (wasIntersecting && !isIntersecting) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we first refactor this so that the Resource is responsible? Eg, we just call setInViewport(isIntersecting) and the Resource can go "oh, shit, I need to build".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like where your head's at, but I definitely disagree with "refactor first". :)

src/service/resources-impl.js Outdated Show resolved Hide resolved
Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on my side. But please take a look at the experiment override question.

src/inabox/inabox-resources.js Outdated Show resolved Hide resolved
Copy link
Contributor

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To give file-size approval. I'll do a full review soon.

@dreamofabear

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants