Skip to content

dom: Implement minimal IntersectionObserver workflow#35551

Merged
gterzian merged 31 commits intoservo:mainfrom
stevennovaryo:intersection-observer-observation-step
Mar 18, 2025
Merged

dom: Implement minimal IntersectionObserver workflow#35551
gterzian merged 31 commits intoservo:mainfrom
stevennovaryo:intersection-observer-observation-step

Conversation

@stevennovaryo
Copy link
Copy Markdown
Contributor

@stevennovaryo stevennovaryo commented Feb 20, 2025

Implement minimal IntersectionObserver processing model which includes the processing model algorithm, omitting the implementation documented in the tracking issue. Most of the major implementations would depends on other API/module.

WPT Result

We are not experiencing timeouts in the many of IntersectionObserver WPT tests that happens because no intersection is reported. Although the intersection reported is not accurate because of the aforementioned reasons. More of "first rAF" started to pass too, presumably because in the initial observations, many of the intersections cases is still not stimulated yet.

Test /intersection-observer/nested-cross-origin-iframe.sub.html produces quite flaky result, either between TIMEOUT or FAIL. Due to scrollIntoView is yet to be implemented, test is supposed to fail. I guess it might be caused by the bad timing of event loop.
Update: this test seems to be quite stable after several additional commits. Still could create a different issue for this specific test.

cc: @xiaochengh

Try


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes are part of Implement IntersectionObserver #31021
  • There are tests for these changes
  • Make sure all todo is documented and summarized accordingly.

@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from 6bcc915 to f63da18 Compare February 21, 2025 06:45
@stevennovaryo stevennovaryo changed the title [WIP] dom: Implement minimal IntersectionObserver workflow dom: Implement minimal IntersectionObserver workflow Feb 25, 2025
@stevennovaryo
Copy link
Copy Markdown
Contributor Author

I guess the changes is fairly complete for reviews. I am still analyzing the test and rechecking the logic and components, but it will be great to get overall review and discussions to make sure I am on the right track.

@stevennovaryo stevennovaryo marked this pull request as ready for review February 25, 2025 07:02
Copy link
Copy Markdown
Member

@gterzian gterzian left a comment

Choose a reason for hiding this comment

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

I haven't reviewed all the logic but to the question as to whether you are on the right track the answer is: yes.

// > Let registration be the IntersectionObserverRegistration record in target’s internal
// > [[RegisteredIntersectionObservers]] slot whose observer property is equal to observer.
// We will clone now to prevent borrow error, and set the value in the end of the loop.
let registration = target
Copy link
Copy Markdown
Member

@gterzian gterzian Feb 25, 2025

Choose a reason for hiding this comment

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

I'm not sure it makes sense to separate the info from the registration, because when implementing step 14 you will need the registration.


/// IntersectionObserverRegistration but the fields other than observer is
/// separated to pass unrooted them with ease.
///
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure this distinction makes sense, because in update_intersection_observations_steps you will need to eventually use both the registration and the info(when implementing what is now a todo).

To make this rootable, you need to use the example of #34194

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure! I believe making it rootable is correct in this scope. I have tried to implement it here bf85252.

After rechecking it, I am actually not sure whether it should have used cloned version or borrow in this case. I believe since observation step should avoid making changes to DOM tree (e.g. reflow), it would be better to use reference instead. a0e8794

@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from 8422a42 to f118ce8 Compare February 28, 2025 04:21
Copy link
Copy Markdown
Member

@gterzian gterzian left a comment

Choose a reason for hiding this comment

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

Looks good to me overall.

Would be good to create a meta issue with a todo list reflecting all the todos in the code.

Perhaps someone more familiar with layout can also take a look? cc @mrobinson @mukilan


/// Whether the observer is connected to the owner document.
/// An observer is connected if it is registered in intersection_observers list inside document.
is_connected: Cell<bool>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems redundant because I think you can deduce that value from observation_targets.

// as well as those for which a rendering update would be unnecessary,
// but this isn't happening here.

// TODO(#31021): the filtering of docs is extended to not exclude the ones that
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this is the right issue; If any it should be #31242

/// > The root intersection rectangle for an IntersectionObserver is
/// > the rectangle we’ll use to check against the targets.
///
/// NOTE: Firefox seems to only account for viewport/boundingBox area without not covered by
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be useful to point out why FF's behavior is relevant here. I assume because we do the same?

Copy link
Copy Markdown
Contributor Author

@stevennovaryo stevennovaryo Feb 28, 2025

Choose a reason for hiding this comment

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

Currently, it is using the viewport that is quite different from the spec and other implementation. It is considering the scroll offset and not considering the size of the scrollbar. I really should add this into another todo also, individually on each impacted lines.

Copy link
Copy Markdown
Contributor Author

@stevennovaryo stevennovaryo Mar 1, 2025

Choose a reason for hiding this comment

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

I have checked this further, it seems that FF behavior make more sense. And, the spec was quite incomplete in this root intersection rectangle section. I believe it should consider clipping and probably several more steps similar to https://w3c.github.io/IntersectionObserver/#compute-the-intersection. I will check chromium's implementation too, as FF is not quite updated regarding several new features.

I will leave viewport addressing identical to element's client-width for now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok, thanks for all the research. For clarity, what I mean is that we should write the comment from the perspective of what Servo is doing, and then perhaps explain that this matches FF's behavior and that the spec has not settled down yet. SO basically the same content, but putting the focus on what we're trying to do.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing out. Sorry for the misunderstanding the intention.

debug!("descendant of containing block chain is not implemented");
}

// NOTE: Firefox skips these steps too, if target_rect or root_bounds is zero.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Following Gecko's behavior and skipping these steps if ... (writing from the point of what we're doing here, not what FF is doing).

Copy link
Copy Markdown
Contributor Author

@stevennovaryo stevennovaryo Mar 1, 2025

Choose a reason for hiding this comment

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

My bad it is more like an todo rather than a note as these is not implemented. It should also mention that it was for null value instead of zero (rectangle?).

I added it in ecc6743, I guess it is better to add it here since it does not make sense to assume null return from getBoundingBox as a zero rectangle.

/// <https://w3c.github.io/IntersectionObserver/#document-intersectionobservertaskqueued>
intersection_observer_task_queued: Cell<bool>,
/// Active intersection observers that should be processed by this document.
intersection_observers: DomRefCell<Vec<Dom<IntersectionObserver>>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

true
}

/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here I don't think the link is useful, because it does not link to anything describing "adding an intersection observer". I think it would be better to point out that this does not follow the spec as per w3c/IntersectionObserver#525

(same below)

// > 2. Let notify list be a list of all IntersectionObservers whose root is in the DOM tree of document.
// > 3. For each IntersectionObserver object observer in notify list, run these steps:
// Iterating a copy of observer stored in the document to prevent borrow error.
for intersection_observer in self.intersection_observers.clone().borrow().iter() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we avoid the cloning by making IntersectionObserver.callback an Rc<RefCell<>>, so that there's internal mutability?

Or at least we can move the cloning to inside invoke_callback so that we don't clone every observer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am not sure, because a callback can modify the self.intersection_observers completely, by using disconnect(), observe(), and unobserve(). I should probably wrote about that in the comment too. We could filter the observer that won't invoke any callback and copy it before any iteration I guess.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see, good point! Thanks!

}

/// Step 3.1-3.5 of <https://w3c.github.io/IntersectionObserver/#notify-intersection-observers-algo>
pub(crate) fn invoke_callback(&self) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should rename this method, because it doesn't always invoke the callback.

Maybe invoke_callback_if_necessary?

@stevennovaryo
Copy link
Copy Markdown
Contributor Author

stevennovaryo commented Feb 28, 2025

Looks good to me overall.

Would be good to create a meta issue with a todo list reflecting all the todos in the code.

Thanks for the review!

Sure, most of the todo should be summarized in the description. I would like to hear from the other, especially about the todos related to layout. If it seems reasonable we could create the meta issue based of that.

@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from dc66d04 to 1793841 Compare February 28, 2025 22:49
@gterzian
Copy link
Copy Markdown
Member

gterzian commented Mar 3, 2025

Looks good to me overall.
Would be good to create a meta issue with a todo list reflecting all the todos in the code.

Thanks for the review!

Sure, most of the todo should be summarized in the description.

Here is an example for ResizeObserver #31182

@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from ddf5905 to 268d92d Compare March 4, 2025 04:06
@stevennovaryo
Copy link
Copy Markdown
Contributor Author

Rebased to recent changes.

@Loirooriol @mrobinson If you are not busy, could you please give a look as well? Thank you. (sorry for the ping)

@jschwe
Copy link
Copy Markdown
Member

jschwe commented Mar 17, 2025

@stevennovaryo Could you rebase this PR again?

Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from 268d92d to f6c5d49 Compare March 17, 2025 17:21
@stevennovaryo
Copy link
Copy Markdown
Contributor Author

@stevennovaryo Could you rebase this PR again?

Rebased, could we get this changes merged soon? Try

@stevennovaryo stevennovaryo force-pushed the intersection-observer-observation-step branch from f6c5d49 to 8fa5559 Compare March 17, 2025 18:08

//! Copy of Stylo Gecko's [`style::values::specified::gecko::IntersectionObserverRootMargin`] implementation.
//! TODO: expose the object to servo as well in Stylo
//! TODO(stevennovaryo): when stylo's IntersectionObserverMargin is up, make a thin wrapper and remove copied codes
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated, Thanks!

)
}

// query content box without considering any reflow
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit:

Suggested change
// query content box without considering any reflow
// Query content box without considering any reflow

Signed-off-by: stevennovaryo <[email protected]>
@gterzian gterzian added this pull request to the merge queue Mar 18, 2025
Merged via the queue into servo:main with commit 67a5f28 Mar 18, 2025
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants