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

Add Scroll Margin to Intersection Observer #511

Merged
merged 15 commits into from
Sep 14, 2023

Conversation

tcaptan-cr
Copy link
Collaborator

@tcaptan-cr tcaptan-cr commented Aug 30, 2023

Closes #431

The following tasks have been completed:

Implementation commitment:


Preview | Diff

@tcaptan-cr
Copy link
Collaborator Author

PTAL @emilio @zcorpan @smfr

@marcoscaceres
Copy link
Member

Thanks @tcaptan-cr... just to make this even easier to review, would you mind splitting out the Makefile into its own PR (and possibly even the xref bikeshed fixes if it's not a huge pain?)

@marcoscaceres
Copy link
Member

marcoscaceres commented Sep 1, 2023

Also, @tcaptan-cr, you are committing to becoming co-Editor? That's ok... but just making sure you are 100% ok with taking on co-ownership. I'll need to announce that to the working group.

@tcaptan-cr
Copy link
Collaborator Author

Thanks @tcaptan-cr... just to make this even easier to review, would you mind splitting out the Makefile into its own PR (and possibly even the xref bikeshed fixes if it's not a huge pain?)

I split the makefile into this PR, and the bikeshed fixes into this PR

@szager-chromium
Copy link
Collaborator

FYI, this change was reviewed in tcaptan's mirror:

tcaptan-cr#1

Copy link
Collaborator

@szager-chromium szager-chromium left a comment

Choose a reason for hiding this comment

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

lgtm

@tcaptan-cr
Copy link
Collaborator Author

Also, @tcaptan-cr, you are committing to becoming co-Editor? That's ok... but just making sure you are 100% ok with taking on co-ownership. I'll need to announce that to the working group.

Yes, I am 100% OK with taking on co-ownership.

Copy link
Collaborator

@emilio emilio 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, with that bit clarified. Thanks for doing this!

index.bs Outdated
@@ -630,9 +673,11 @@ run these steps:
of the {{document}}, and update |container| to be
the <a>browsing context container</a> of |container|.
2. Map |intersectionRect| to the coordinate space of |container|.
3. If |container| has a <a>content clip</a> or a css <a>clip-path</a> property,
3. If |container| is scrollable, apply the {{IntersectionObserver}}’s
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be great to have a more precise definition of scrollable, or a link to the relevant CSS spec. Otherwise whether this applies to overflow: hidden or not is a bit questionable, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. How about using CSS scroll-container?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sounds good, let's make sure that we test overflow: hidden then, and confirm that it does apply.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks Emilio!
The tests I added in my PR for the wpt tests use overflow: hidden. For example the scroll-margin.html test.
I confirmed these pass with my prototype.

Copy link
Collaborator

@emilio emilio left a comment

Choose a reason for hiding this comment

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

Let's fix the link. Pretty sure there's a better way of doing this.

Same for the margin property link, which I hadn't noticed.

Also, can you rebase to remove the merge commit?

index.bs Outdated Show resolved Hide resolved
@tcaptan-cr
Copy link
Collaborator Author

Let's fix the link. Pretty sure there's a better way of doing this.

Same for the margin property link, which I hadn't noticed.

Also, can you rebase to remove the merge commit?

Done.

@tcaptan-cr
Copy link
Collaborator Author

lgtm

Thanks Stefan!

@tcaptan-cr
Copy link
Collaborator Author

Looks good to me, with that bit clarified. Thanks for doing this!

Thanks Emilio!

@tcaptan-cr tcaptan-cr merged commit f22fbf4 into w3c:main Sep 14, 2023
2 checks passed
github-actions bot added a commit that referenced this pull request Sep 14, 2023
SHA: f22fbf4
Reason: push, by tcaptan-cr

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@letitz
Copy link
Member

letitz commented Sep 19, 2023

Hi there,

Chromium web platform security reviewer here. I've been looking at the I2P for this change and have a few questions, mostly to clarify my understanding:

  1. Why does this rely on same-origin-domain-ness of elements? I see the spec mentions that this only matters for implicit intersection observers, but I don't have context on why this matters (or why origin-domain was chosen over origin, the usual security boundary).

  2. I've given this some thought through the lens of xs-leaks, and I believe this makes no material difference to that kind of attack, mostly because scrollMargin does not apply to iframes. This means that even if the intersection rectangle has been expanded by scrollMargin, it will be clipped to the iframe's viewport on the way up the tree, so a malicious iframe will not be able to determine more than "am I visible" (modulo rootMargin, but it only applies for same-origin-domain elements). Does this match your understanding?
    I guess the iframe could be contained in a scroll container itself, and scrollMargin would affect that? This seems related to my next question:

  3. In Ability to have automatic rootMargin for all nested scroll containers #431 (comment) it was mentioned that scrollMargin should stop at cross-origin iframe boundaries. What was meant by that, and is that specified in this PR? I cannot find a reference.

  4. I cannot find in the spec algorithms where it is specified that scrollMargin only applies to same-origin-domain elements. Is the plain english paragraph normative here?

@szager-chromium
Copy link
Collaborator

szager-chromium commented Sep 20, 2023

This feature is modelled after the existing rootMargin feature, and it has basically the same set of security/privacy concerns...

  1. Only implicit-root observers are subject to these restrictions, because only implicit-root observers communicate geometry information that crosses iframe boundaries. Quoting from the spec:

"Any target of an explicit root observer is also a same-origin-domain target, since the target must be in the same document as the intersection root."

  1. scrollMargin does indeed apply to the top-level scrolling viewport of the iframe! No distinction is made between the document-level scrolling viewport and a scrollable element. That is why scrollMargin is restricted to explicit-root observers and same-origin-domain implicit-root observers.

  2. The comment in that spec issue is simply stating the requirement that scrollMargin cannot probe the geometry of the embedding page, in the same way that rootMargin cannot. This is commensurate with the above explanations.

  3. Beginning at line 291 in index.bs, this PR adds:

"These offsets are only applied when handling same-origin-domain targets;
for cross-origin-domain targets they are ignored.
"

@letitz
Copy link
Member

letitz commented Sep 22, 2023

Thanks for the answers!

This feature is modelled after the existing rootMargin feature, and it has basically the same set of security/privacy concerns...

Right, and I don't have context on those concerns, hence my questions :)

  1. Only implicit-root observers are subject to these restrictions, because only implicit-root observers communicate geometry information that crosses iframe boundaries. Quoting from the spec:

"Any target of an explicit root observer is also a same-origin-domain target, since the target must be in the same document as the intersection root."

I see, and this is actually specified by step 2.2.2 of the Run the Update Intersection Observations Steps algorithm.

I'm still left with my questions:

  1. Why does this matter (for explicit root observers)?
  2. Why choose same-origin-domain instead of same-origin? The latter is the web's usual security boundary.
  1. scrollMargin does indeed apply to the top-level scrolling viewport of the iframe! No distinction is made between the document-level scrolling viewport and a scrollable element. That is why scrollMargin is restricted to explicit-root observers and same-origin-domain implicit-root observers.

Oh, I see. I was looking at step 3.1 of the compute the intersection algorithm, but step 3.3 also applies because an iframe is a scroll container?

  1. The comment in that spec issue is simply stating the requirement that scrollMargin cannot probe the geometry of the embedding page, in the same way that rootMargin cannot. This is commensurate with the above explanations.

I see, this is what is mentioned in the Privacy and Security section. Can you help me understand the attack here? How would the embeddee learn about its embedder's geometry using rootMargin or scrollMargin?

That section mentions that IntersectionObserver does not allow new methods of detecting intersection, only making existing hacks more usable. Is this "compatibility goal" what led to the choice of same-origin-domain instead of same-origin?

  1. Beginning at line 291 in index.bs, this PR adds:

"These offsets are only applied when handling same-origin-domain targets; for cross-origin-domain targets they are ignored."

Right, this was what I referred to as "the plain english paragraph". IIUC this does not correspond to any step in the compute the intersection algorithm? I was expecting to find something there.

@szager-chromium
Copy link
Collaborator

  1. Only implicit-root observers are subject to these restrictions, because only implicit-root observers communicate geometry information that crosses iframe boundaries. Quoting from the spec:

"Any target of an explicit root observer is also a same-origin-domain target, since the target must be in the same document as the intersection root."

I see, and this is actually specified by step 2.2.2 of the Run the Update Intersection Observations Steps algorithm.

I'm still left with my questions:

  1. Why does this matter (for explicit root observers)?

Sorry, I don't understand your question. Why does what matter for explicit root observers? Explicit root observers can use scrollMargin without restriction; implicit root observers can use scrollMargin conditionally.

  1. Why choose same-origin-domain instead of same-origin? The latter is the web's usual security boundary.

Here's the previous discussion.

  1. scrollMargin does indeed apply to the top-level scrolling viewport of the iframe! No distinction is made between the document-level scrolling viewport and a scrollable element. That is why scrollMargin is restricted to explicit-root observers and same-origin-domain implicit-root observers.

Oh, I see. I was looking at step 3.1 of the compute the intersection algorithm, but step 3.3 also applies because an iframe is a scroll container?

Yes, that's right.

  1. The comment in that spec issue is simply stating the requirement that scrollMargin cannot probe the geometry of the embedding page, in the same way that rootMargin cannot. This is commensurate with the above explanations.

I see, this is what is mentioned in the Privacy and Security section. Can you help me understand the attack here? How would the embeddee learn about its embedder's geometry using rootMargin or scrollMargin?

The general idea is that the attacker would create a bunch of IntersectionObservers, each with slightly different rootMargin and perhaps a range of thresholds; and that by analyzing the received notifications, it would be possible to deduce the size of the containing viewport. I unfortunately can't find a concrete exploit, but this came up during the early days of developing the API.

  1. Beginning at line 291 in index.bs, this PR adds:

"These offsets are only applied when handling same-origin-domain targets; for cross-origin-domain targets they are ignored."

Right, this was what I referred to as "the plain english paragraph". IIUC this does not correspond to any step in the compute the intersection algorithm? I was expecting to find something there.

Ah, that's a good point. There is an explanation of the same-origin-domain restriction on rootMargin which is reachable from the algorithm description; but the equivalent explanation for scrollMargin is not directly reachable from the algorithm description. I think this content, which is currently intermingled with the definition of root intersection rect, should be moved to the definition of scrollMargin:

When calculating a scrollport intersection rectangle for a same-origin-domain target, the rectangle is expanded according to the offsets in the IntersectionObserver’s [[scrollMargin]] slot in a manner similar to CSS’s margin property, with the four values indicating the amount the top, right, bottom, and left edges, respectively, are offset by, with positive lengths indicating an outward offset. Percentages are resolved relative to the width of the undilated rectangle.

NOTE: scrollMargin affects the clipping of target by all scrollable ancestors up to and including the intersection root. Both the scrollMargin and the rootMargin are applied to a scrollable intersection root’s rectangle.

@zcorpan
Copy link
Member

zcorpan commented Sep 27, 2023

On same-origin-domain vs same-origin, it only matters when using document.domain (which is going away). If the pages involved have used document.domain so they're same-origin-domain, they can access each other's DOM.

@letitz
Copy link
Member

letitz commented Sep 28, 2023

Thanks for the answers and pointers, this is super helpful.

Using same-origin-domain makes sense for this, though it would always be better to avoid introducing a new dependency on document.domain. If y'all would be happy with same-origin, I think it would be a nicer fit for today's web platform.

+1 to amending the spec to extend the same-origin-domain restriction from rootMargin to scrollMargin more explicitly.

I have one remaining clarification question: the intent of the same-origin-domain restriction is to prevent scrollMargin from applying to scroll containers (including iframe documents) that are cross-origin-domain with the observer, right? That is, if A embeds B embeds A, the grandchild A cannot learn about the geometry of B by using scrollMargin, even though it is same-origin with the top-level document. Is that correct?

@progers
Copy link

progers commented Oct 10, 2023

I don't think there is any new information being exposed that is not already in rootMargin. Do you think there may be something?

the intent of the same-origin-domain restriction is to prevent scrollMargin from applying to scroll containers (including iframe documents) that are cross-origin-domain with the observer, right?

Yes, this is my understanding.

@letitz
Copy link
Member

letitz commented Oct 12, 2023

I don't think there is any new information being exposed that is not already in rootMargin. Do you think there may be something?

I think I'm now convinced there isn't. Since scrollMargin applies to many more elements than rootMargin, I was worried there could be.

the intent of the same-origin-domain restriction is to prevent scrollMargin from applying to scroll containers (including iframe documents) that are cross-origin-domain with the observer, right?

Yes, this is my understanding.

Thanks for confirming.

@zcorpan
Copy link
Member

zcorpan commented Oct 19, 2023

There were no bugs filed for Gecko or WebKit. Please keep that in the issue template in the future.

I've now filed:

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.

Ability to have automatic rootMargin for all nested scroll containers
7 participants