|
|
Created:
5 years, 1 month ago by szager1 Modified:
4 years, 11 months ago CC:
blink-reviews, blink-reviews-bindings_chromium.org, blink-reviews-dom_chromium.org, Inactive, chromium-reviews, dglazkov+blink, eae+blinkwatch, rwlbuis, sof, site-isolation-reviews_chromium.org, vivekg_samsung, vivekg Base URL:
https://chromium.googlesource.com/chromium/src@master Target Ref:
refs/pending/heads/master Project:
chromium Visibility:
Public. |
DescriptionIntersectionObserver: second cut.
All major features are implemented, a few TODO's, and a few tests.
Design doc:
https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnLiQ/edit
BUG=540528
Committed: https://crrev.com/b5cff0041ec225f4eaac870e87d6189321754983
Cr-Commit-Position: refs/heads/master@{#368458}
Patch Set 1 #Patch Set 2 : Clarify the tear-down path for observers and observations. #
Total comments: 39
Patch Set 3 : oilpan-ify #
Total comments: 6
Patch Set 4 : Use existing Document::m_weakFactory. #
Total comments: 2
Patch Set 5 : Oilpan-ed and kinda works #Patch Set 6 : Refactor clipping code #
Total comments: 2
Patch Set 7 : Added tests, fixed intersection computation. #Patch Set 8 : Fix rootMargin parsing #
Total comments: 8
Patch Set 9 : Implemented root margin #
Total comments: 241
Patch Set 10 : Comments addressed, every last one. #Patch Set 11 : Added dispose() methods for expicit cleanup #
Total comments: 66
Patch Set 12 : Second round of esprehn comments addressed. #Patch Set 13 : Add missing break, rebaseline, no config.h #
Total comments: 102
Patch Set 14 : Addressed haraken's comments #Patch Set 15 : Addressed latest comments from esprehn #Patch Set 16 : Remove bad ASSERT #
Total comments: 18
Patch Set 17 : sigbjornf nits #
Total comments: 37
Patch Set 18 : haraken nits #
Total comments: 2
Patch Set 19 : split out suspend/resume and root margin #Patch Set 20 : Fix bad edit. #Patch Set 21 : clean up teardown logic #
Total comments: 15
Patch Set 22 : more haraken nits #Patch Set 23 : rebaseline #
Total comments: 2
Patch Set 24 : header nit, fix test expectation #Patch Set 25 : test expectation #Patch Set 26 : Add RuntimeEnabled flags to all idl's, fix test expectations. #
Total comments: 2
Messages
Total messages: 106 (16 generated)
Description was changed from ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. BUG= ========== to ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. BUG= ==========
szager@chromium.org changed reviewers: + eae@chromium.org, mpb@chromium.org, ojan@chromium.org
Here it is: the basic architecture is there, but I literally haven't tried running an executable with it yet. I started a design doc for the implementation: https://docs.google.com/a/google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYC...
szager@chromium.org changed reviewers: + haraken@chromium.org
szager@chromium.org changed reviewers: - haraken@chromium.org
haraken@chromium.org changed reviewers: + haraken@chromium.org
Here is a first round of comments :) - You don't need to use WillBe types for newly introduced classes. You can just use Oilpan types. It will make the CL much simpler. - Please confirm that the CL compiles with GYP_DEFINES='blink_gc_plugin=1 enable_oilpan=1' - Regarding V8-Blink bindings, I guess you mimiced the implementation of PerformanceObserver (or MutationObserver), right? The lifetime management is quite complicated, which I'll take a closer look in the next round. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h:5: // This file has been auto-generated by code_generator_v8.py. DO NOT MODIFY! Remove this comment. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:18: class IntersectionObservation : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObservation> { You don't need to use WillBe types for newly introduced classes. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservationRegistry.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservationRegistry.cpp:75: m_intersectionObservations.add(adoptRef(&observation)); adoptRefWillBeNoop ? Otherwise, it won't compile with GYP_DEFINES='enable_oilpan=1 blink_gc_plugin=1'. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:255: // visitor->trace(m_observations); You need to use: #if ENABLE(OILPAN) visitor->trace(m_observations) #endif But I'd recommend you just make IntersectionObserver GarbageCollected. Then you don't need the #if ENABLE(OILPAN). https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.h:26: class IntersectionObserver final : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObserver>, public ScriptWrappable { You don't need to use WillBe types. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.h:54: EAGERLY_FINALIZE(); I guess you don't need this. The destructor is not touching any on-heap object, as far as I see. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:18: class IntersectionObserverCallback : public NoBaseWillBeGarbageCollectedFinalized<IntersectionObserverCallback> { You don't need to use WillBe types for newly introduced classes. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:17: class IntersectionObserverEntry : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObserverEntry>, public ScriptWrappable { You don't need to use WillBe types for newly introduced classes. You can just make it GarbageCollected and avoid introducing WillBe types. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl:8: WillBeGarbageCollected, I think you can just use GarbageCollected.
haraken@chromium.org changed reviewers: + oilpan-reviews@chromium.org
First pass, still trying to wrap my head around some of the logic. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Layo... File third_party/WebKit/LayoutTests/fast/dom/intersection-observer.html (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Layo... third_party/WebKit/LayoutTests/fast/dom/intersection-observer.html:61: </script> This test is pretty sad as it doesn't tests much. Also try to avoid data driven tests as those are hard to reason about and debug. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:1462: observation->setActive(observation->observer()->checkTargetHierarchy(this)); If I read this correctly this iterates over all observers and sets them active if they're in the same frame as |this|? What does active mean in this case? That it can fire or that it is visible? https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:1544: for (auto& observation: data->intersectionObservations()) { data->deactiveAllObservations()? https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:3623: // TODO: support zero-size targetRect How would you intersect with a zero-sized target? https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:3633: layoutObject()->mapRectToPaintInvalidationBacking(toLayoutBoxModelObject(root->layoutObject()), intersectionRect, nullptr); This doesn't seem right, what are you trying to do here? Something like the following seems like it should work and be a bit simpler: Vector<FloatQuad> quads; layoutObject()->absoluteQuads(quads); document().adjustFloatQuadsForScrollAndAbsoluteZoom(quads, layoutObject()); https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:74: observer()->enqueueIntersectionObserverEntry(IntersectionObserverEntry::create(timestamp, pixelSnappedIntRect(targetRect), pixelSnappedIntRect(rootRect), pixelSnappedIntRect(intersectionRect), target())); Please wrap this line, 212 columns is a bit too wide. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:28: return DefaultHash<HashKey>::Hash::hash(HashKey(nullptr, nullptr)); A separate hash method that takes no type would make this clearer. static unsigned hash() { return DefaultHash<HashKey>::Hash::hash(HashKey(nullptr, nullptr)); } static unsigned hash(PeekInType keyRef) { IntersectionObservation* key = keyRef; return DefaultHash<HashKey>::Hash::hash(HashKey(key->observer(), key ->target())); } https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:31: static bool equal(PeekInType aRef, PeekInType bRef) You might want to use const here. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:41: rootMargin = static_cast<int>(token.numericValue()); Is flooring the number the desired result? If so could you make it explicit? https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:102: bool IntersectionObserver::checkTargetHierarchy(Element* target) hasSameRoot or sharesAncestor might be better names. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.idl (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.idl:8: // https://github.com/slightlyoff/IntersectionObserver This needs to point to something a bit more official than an individuals git hub page.
https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:18: class IntersectionObservation : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObservation> { On 2015/11/16 00:41:34, haraken wrote: > > You don't need to use WillBe types for newly introduced classes. If I understand you correctly, I can use regular oilpan types (Member, RawPtr, etc.) for newly-created classes, but I should continue to use WillBe types for existing DOM classes. So, I won't change this member declaration in class IntersectionObservation: WeakPtrWillBeWeakMember<Element> m_target; Yes?
On 2015/11/17 01:50:04, szager1 wrote: > https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... > File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): > > https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... > third_party/WebKit/Source/core/dom/IntersectionObservation.h:18: class > IntersectionObservation : public > RefCountedWillBeGarbageCollectedFinalized<IntersectionObservation> { > On 2015/11/16 00:41:34, haraken wrote: > > > > You don't need to use WillBe types for newly introduced classes. > > If I understand you correctly, I can use regular oilpan types (Member, RawPtr, > etc.) for newly-created classes, but I should continue to use WillBe types for > existing DOM classes. So, I won't change this member declaration in class > IntersectionObservation: > > WeakPtrWillBeWeakMember<Element> m_target; > > Yes? Yes, you need to use WillBe types for pointers to existing DOM classes. My point is just that you don't need to use WillBe types for classes you introduce from now :)
I'm working on making the new types garbage collected... https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Layo... File third_party/WebKit/LayoutTests/fast/dom/intersection-observer.html (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Layo... third_party/WebKit/LayoutTests/fast/dom/intersection-observer.html:61: </script> On 2015/11/16 23:34:44, eae wrote: > This test is pretty sad as it doesn't tests much. Also try to avoid data driven > tests as those are hard to reason about and debug. Agreed, this test needs some work. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h:5: // This file has been auto-generated by code_generator_v8.py. DO NOT MODIFY! On 2015/11/16 00:41:34, haraken wrote: > > Remove this comment. > Done in forthcoming next patch. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:1462: observation->setActive(observation->observer()->checkTargetHierarchy(this)); On 2015/11/16 23:34:44, eae wrote: > If I read this correctly this iterates over all observers and sets them active > if they're in the same frame as |this|? What does active mean in this case? That > it can fire or that it is visible? In this case, "active" means that |this| is a descendant of the observer's root, which is what observer()->checkTargetHierarchy(this) calculates. Every time a target element is removed from the DOM tree, all of its observations are deactivated. When the target is re-added to the DOM tree, we need to reevaluate the hierarchical relationship between target and root for each of its observations, and reactive the observations for which the relationship is valid. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:1544: for (auto& observation: data->intersectionObservations()) { On 2015/11/16 23:34:44, eae wrote: > data->deactiveAllObservations()? Done in forthcoming patch. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:3623: // TODO: support zero-size targetRect On 2015/11/16 23:34:44, eae wrote: > How would you intersect with a zero-sized target? The proposal is that we would calculate clipping by checking whether the layout location point of the target is inside the clip rect of each of its ancestors, up to the root. If it is inside, we would call that a visibleRatio of 1; if it's not inside, visibleRatio is 0. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:3633: layoutObject()->mapRectToPaintInvalidationBacking(toLayoutBoxModelObject(root->layoutObject()), intersectionRect, nullptr); On 2015/11/16 23:34:44, eae wrote: > This doesn't seem right, what are you trying to do here? > > Something like the following seems like it should work and be a bit simpler: > > Vector<FloatQuad> quads; > layoutObject()->absoluteQuads(quads); > document().adjustFloatQuadsForScrollAndAbsoluteZoom(quads, layoutObject()); My understanding is that mapRectToPaintInvalidationBacking will do all of the necessary clipping of intersectionRect, all the way through the hierarchy between |this| and |root|. This may not be exactly what we want, and it doesn't support the rootMargin feature of intersection observer, but it's just a rough cut to get something working. Added a TODO https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:28: return DefaultHash<HashKey>::Hash::hash(HashKey(nullptr, nullptr)); On 2015/11/16 23:34:44, eae wrote: > A separate hash method that takes no type would make this clearer. > > static unsigned hash() > { > return DefaultHash<HashKey>::Hash::hash(HashKey(nullptr, nullptr)); > } > > static unsigned hash(PeekInType keyRef) > { > IntersectionObservation* key = keyRef; > return DefaultHash<HashKey>::Hash::hash(HashKey(key->observer(), key > ->target())); > } Do you mean: static unsigned hash(PeekInType keyRef) { IntersectionObservation* key = keyRef; if (!key) return hash(); return DefaultHash<HashKey>::Hash::hash(HashKey(key->observer(), key->target())); } ? Otherwise, I don't know when the no-argument version would ever get called. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservation.h:31: static bool equal(PeekInType aRef, PeekInType bRef) On 2015/11/16 23:34:44, eae wrote: > You might want to use const here. Done in next patch. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObservationRegistry.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObservationRegistry.cpp:75: m_intersectionObservations.add(adoptRef(&observation)); On 2015/11/16 00:41:34, haraken wrote: > > adoptRefWillBeNoop ? Otherwise, it won't compile with > GYP_DEFINES='enable_oilpan=1 blink_gc_plugin=1'. Fixed in next patch (either with adoptRefWillBeNoop, or by switching to GarbageCollected). https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:41: rootMargin = static_cast<int>(token.numericValue()); On 2015/11/16 23:34:44, eae wrote: > Is flooring the number the desired result? If so could you make it explicit? Done in next patch. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:102: bool IntersectionObserver::checkTargetHierarchy(Element* target) On 2015/11/16 23:34:45, eae wrote: > hasSameRoot or sharesAncestor might be better names. Maybe isValidTarget? Or isDescendantOfRoot? I'll go with isDescendantOfRoot. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:255: // visitor->trace(m_observations); On 2015/11/16 00:41:34, haraken wrote: > > You need to use: > > #if ENABLE(OILPAN) > visitor->trace(m_observations) > #endif > > But I'd recommend you just make IntersectionObserver GarbageCollected. Then you > don't need the #if ENABLE(OILPAN). Working on it. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.h:26: class IntersectionObserver final : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObserver>, public ScriptWrappable { On 2015/11/16 00:41:35, haraken wrote: > > You don't need to use WillBe types. Working on this... https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.h:54: EAGERLY_FINALIZE(); On 2015/11/16 00:41:35, haraken wrote: > > I guess you don't need this. The destructor is not touching any on-heap object, > as far as I see. > Done. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserver.idl (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserver.idl:8: // https://github.com/slightlyoff/IntersectionObserver On 2015/11/16 23:34:45, eae wrote: > This needs to point to something a bit more official than an individuals git hub > page. I don't think we have anything more official right now; I'll add a TODO to update the comment. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:18: class IntersectionObserverCallback : public NoBaseWillBeGarbageCollectedFinalized<IntersectionObserverCallback> { On 2015/11/16 00:41:35, haraken wrote: > > You don't need to use WillBe types for newly introduced classes. Working on it. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:17: class IntersectionObserverEntry : public RefCountedWillBeGarbageCollectedFinalized<IntersectionObserverEntry>, public ScriptWrappable { On 2015/11/16 00:41:35, haraken wrote: > > You don't need to use WillBe types for newly introduced classes. You can just > make it GarbageCollected and avoid introducing WillBe types. > Working on it. https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl (right): https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl:8: WillBeGarbageCollected, On 2015/11/16 00:41:35, haraken wrote: > > I think you can just use GarbageCollected. Working on it.
> https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... > third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:17: class > IntersectionObserverEntry : public > RefCountedWillBeGarbageCollectedFinalized<IntersectionObserverEntry>, public > ScriptWrappable { > On 2015/11/16 00:41:35, haraken wrote: > > > > You don't need to use WillBe types for newly introduced classes. You can just > > make it GarbageCollected and avoid introducing WillBe types. > > > > Working on it. > > https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... > File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl (right): > > https://codereview.chromium.org/1449623002/diff/20001/third_party/WebKit/Sour... > third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl:8: > WillBeGarbageCollected, > On 2015/11/16 00:41:35, haraken wrote: > > > > I think you can just use GarbageCollected. > > Working on it. Thanks for working on Oilpan. These materials will be helpful to learn about Oilpan's programming: - A slide used in a hand-on session at BlinkOn: https://docs.google.com/presentation/d/1XPu03ymz8W295mCftEC9KshH9Icxfq81YwIJQ... - A comprehensive documentation: https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/So...
Oilpan-ified. PTAL
dcheng@chromium.org changed reviewers: + dcheng@chromium.org
https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Document.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Document.h:1401: OwnPtrWillBeMember<WeakPtrFactory<Document>> m_weakPointerFactory; WeakPtrFactory is not a garbage collected object, so this should just be an OwnPtr. Oilpan's WeakMembers, etc. are completely different from WeakPtr (which is really basically the same thing as base::WeakPtrFactoryin Chrome). https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.h:543: WeakPtr<Element> createWeakPtr(); That being said, can we do this without exposing weak pointers for Document/Element? For Document, it feels weird, because there's already DocumentLifecycleObserver. For elements, it seems unnecessary as well: from what I can tell (https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#removin...), unregistering observers is tied to removing nodes from the DOM. In that case, wouldn't Element::detach() be the place to do the unregistration?
https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Document.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Document.h:1401: OwnPtrWillBeMember<WeakPtrFactory<Document>> m_weakPointerFactory; On 2015/11/20 20:35:07, dcheng wrote: > WeakPtrFactory is not a garbage collected object, so this should just be an > OwnPtr. Oilpan's WeakMembers, etc. are completely different from WeakPtr (which > is really basically the same thing as base::WeakPtrFactoryin Chrome). I just noticed that Document already has an m_weakFactory member; I'm going to use that instead, and #if ENABLE(OILPAN) appropriately. https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.h:543: WeakPtr<Element> createWeakPtr(); On 2015/11/20 20:35:07, dcheng wrote: > That being said, can we do this without exposing weak pointers for > Document/Element? > > For Document, it feels weird, because there's already DocumentLifecycleObserver. > For elements, it seems unnecessary as well: from what I can tell > (https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#removin...), > unregistering observers is tied to removing nodes from the DOM. In that case, > wouldn't Element::detach() be the place to do the unregistration? The spec is out-of-date; the consensus is now: - when either the root or target element is removed from the DOM, its RegisteredIntersectionObservers will be disabled, but not unobserved. - when root or target is re-added to the DOM, its RegisteredIntersectionObservers will be examined and any one that now has an appropriate inheritance relationship between root and target will be reactivated. Basically, that means that the lifetime of the observer is tied to the lifetime of the root and target elements, which may be in different documents.
https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.h:543: WeakPtr<Element> createWeakPtr(); On 2015/11/20 at 22:02:23, szager1 wrote: > On 2015/11/20 20:35:07, dcheng wrote: > > That being said, can we do this without exposing weak pointers for > > Document/Element? > > > > For Document, it feels weird, because there's already DocumentLifecycleObserver. > > For elements, it seems unnecessary as well: from what I can tell > > (https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#removin...), > > unregistering observers is tied to removing nodes from the DOM. In that case, > > wouldn't Element::detach() be the place to do the unregistration? > > The spec is out-of-date; the consensus is now: > > - when either the root or target element is removed from the DOM, its RegisteredIntersectionObservers will be disabled, but not unobserved. > - when root or target is re-added to the DOM, its RegisteredIntersectionObservers will be examined and any one that now has an appropriate inheritance relationship between root and target will be reactivated. > > Basically, that means that the lifetime of the observer is tied to the lifetime of the root and target elements, which may be in different documents. In general, I'm not thrilled about exposing weak pointers externally to a class and I'm still not convinced it's necessary here. Where's the most up-to-date spec? And who should I bug to answer random questions about the spec? https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:176: disconnectIntersectionObservations(); This needs to be inside the #if !ENABLE(OILPAN) block. Destructors of heap-allocated objects cannot touch heap-allocated objects (since the other heap-allocated objects may have already been destroyed).
On 2015/11/20 22:50:53, dcheng wrote: > > In general, I'm not thrilled about exposing weak pointers externally to a class > and I'm still not convinced it's necessary here. Where's the most up-to-date > spec? And who should I bug to answer random questions about the spec? > You can bug me or ojan about the spec. The difficult situation for garbage collection is that if the document associated with either the root or target of an observation "goes away", the observation could be the thing that prevents the entire document's DOM tree from getting garbage collected (because another element in a different document still points to the observation). As I see it, either we need to do explicit lifecycle management of the observations (yuck), or we need to use weak pointers. I am wide open to other suggestions. > https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... > File third_party/WebKit/Source/core/dom/Element.cpp (right): > > https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... > third_party/WebKit/Source/core/dom/Element.cpp:176: > disconnectIntersectionObservations(); > This needs to be inside the #if !ENABLE(OILPAN) block. Destructors of > heap-allocated objects cannot touch heap-allocated objects (since the other > heap-allocated objects may have already been destroyed). Hmm, I see. I guess that just means I have to do more careful NULL-checking of the WeakMember<Element> fields of observer/observation.
PTAL I cleaned up the reference model, in the code and in the design doc: https://docs.google.com/a/google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYC... https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.h:543: WeakPtr<Element> createWeakPtr(); On 2015/11/20 22:50:52, dcheng wrote: > On 2015/11/20 at 22:02:23, szager1 wrote: > > On 2015/11/20 20:35:07, dcheng wrote: > > > That being said, can we do this without exposing weak pointers for > > > Document/Element? > > > > > > For Document, it feels weird, because there's already > DocumentLifecycleObserver. > > > For elements, it seems unnecessary as well: from what I can tell > > > > (https://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#removin...), > > > unregistering observers is tied to removing nodes from the DOM. In that > case, > > > wouldn't Element::detach() be the place to do the unregistration? > > > > The spec is out-of-date; the consensus is now: > > > > - when either the root or target element is removed from the DOM, its > RegisteredIntersectionObservers will be disabled, but not unobserved. > > - when root or target is re-added to the DOM, its > RegisteredIntersectionObservers will be examined and any one that now has an > appropriate inheritance relationship between root and target will be > reactivated. > > > > Basically, that means that the lifetime of the observer is tied to the > lifetime of the root and target elements, which may be in different documents. > > In general, I'm not thrilled about exposing weak pointers externally to a class > and I'm still not convinced it's necessary here. Where's the most up-to-date > spec? And who should I bug to answer random questions about the spec? I updated the implementation design doc: https://docs.google.com/a/google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYC... Hopefully that clears up the need for weak pointers. https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/core/dom/Element.cpp:176: disconnectIntersectionObservations(); On 2015/11/20 22:50:52, dcheng wrote: > This needs to be inside the #if !ENABLE(OILPAN) block. Destructors of > heap-allocated objects cannot touch heap-allocated objects (since the other > heap-allocated objects may have already been destroyed). Removed this line, so now there is a possibility of a memory leak. It seems like something is missing in oilpan if I can't do necessary cleanup in the destructor.
szager@chromium.org changed reviewers: + adamk@chromium.org, esprehn@chromium.org
Adding adamk and esprehn as reviewerss
Added tests. I'd like to get this landed, please take a look.
I'm mostly reviewing this from a 'lifetime of things' perspective. I would really like to see this move to an explicit cleanup model, because that makes things much easier to understand. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4965: WeakPtrWillBeRawPtr<Document> Document::createWeakPtr() Having look at the design doc and spec some more, I still feel pretty strongly that we should not be exposing weak pointers to Document (more widely) and Node (at all). https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:13: IntersectionObservation::IntersectionObservation(IntersectionObserver& observer, Element* target) target can't be null here, so make it a reference to make that clear. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:34: if (!targetElement || !isActive()) I don't see how this observation is ever cleaned up in the 'element was removed and gced' case. It seems like it depends on the actual IntersectionObserver to be cleaned up. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:136: checkRootAndDetachIfNecessary(); Similarly, I don't think weak pointers is making this simpler. Now, in each method, we need to check if we've already been detached and make sure to clean up in that case. This makes it hard to understand when the cleanup actually runs. It's very much Blink style to explicitly detach things when needed: there's a lot of existing code that does this (LocalFrame<->FrameView, WebLocalFrameImpl, and many other things) and I think we should continue using that pattern when possible. The one potential problem for IntersectionObserver with Oilpan is that you'd want to null out m_root on Document destruction. This can't happen in ~Document, since IntersectionObserver is also a heap object. The good news is that Documents can't be reattached, so that work can just be done in Document::detach() instead.
https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4965: WeakPtrWillBeRawPtr<Document> Document::createWeakPtr() On 2015/12/10 00:37:09, dcheng wrote: > Having look at the design doc and spec some more, I still feel pretty strongly > that we should not be exposing weak pointers to Document (more widely) and Node > (at all). I understand your reticence, but I also spent two weeks trying to figure out a better way to do this, without success. I am open to concrete suggestions. As the design doc points out, there are some unusual lifetime constraints; namely: - IntersectionObservers must remain alive even when there are no javascript references to them. - IntersectionObservers must not keep their root or target elements alive. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:13: IntersectionObservation::IntersectionObservation(IntersectionObserver& observer, Element* target) On 2015/12/10 00:37:09, dcheng wrote: > target can't be null here, so make it a reference to make that clear. That will be in the next patch. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:34: if (!targetElement || !isActive()) On 2015/12/10 00:37:09, dcheng wrote: > I don't see how this observation is ever cleaned up in the 'element was removed > and gced' case. It seems like it depends on the actual IntersectionObserver to > be cleaned up. The target element is the only thing that holds a hard reference to the IntersectionObservation. If the target element gets gc'ed, then the observation will get gc'ed at the same time. https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:136: checkRootAndDetachIfNecessary(); On 2015/12/10 00:37:09, dcheng wrote: > Similarly, I don't think weak pointers is making this simpler. Now, in each > method, we need to check if we've already been detached and make sure to clean > up in that case. This makes it hard to understand when the cleanup actually > runs. > > It's very much Blink style to explicitly detach things when needed: there's a > lot of existing code that does this (LocalFrame<->FrameView, WebLocalFrameImpl, > and many other things) and I think we should continue using that pattern when > possible. > > The one potential problem for IntersectionObserver with Oilpan is that you'd > want to null out m_root on Document destruction. This can't happen in ~Document, > since IntersectionObserver is also a heap object. The good news is that > Documents can't be reattached, so that work can just be done in > Document::detach() instead. The correct place to do this cleanup is when the root element is gc'ed. Unfortunately, there's no way in oilpan to do anything meaningful like this in a destructor. Were it possible to do this in the Element destructor, I could get rid of checkRootAndDetachIfNecessary entirely. Alas.
On 2015/12/10 22:38:55, szager1 wrote: > https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... > File third_party/WebKit/Source/core/dom/Document.cpp (right): > > https://codereview.chromium.org/1449623002/diff/140001/third_party/WebKit/Sou... > third_party/WebKit/Source/core/dom/Document.cpp:4965: > WeakPtrWillBeRawPtr<Document> Document::createWeakPtr() > On 2015/12/10 00:37:09, dcheng wrote: > > Having look at the design doc and spec some more, I still feel pretty strongly > > that we should not be exposing weak pointers to Document (more widely) and > Node > > (at all). > > I understand your reticence, but I also spent two weeks trying to figure out a > better way to do this, without success. BTW, this is not solely my conclusion; I talked to ojan and adamk about this, ad nauseum.
https://codereview.chromium.org/1449623002/diff/100001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/100001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.h:547: bool computeIntersection(Element*, LayoutRect&, LayoutRect&, LayoutRect&); const, also const Element* hopefully. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:2: // enough to screw with frame offsets that are measured by the test. Delay all that hmmm... https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:23: "\nintersectionRect=" + rectToString(entry.intersectionRect) + I'd put each of these on a line, and put a separate "\n" so the "foo=" blocks line up https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:39: function rectToString(rect) { defined twice in this file https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:51: checkRect(expected['boundingClientRect'], actual + ".boundingClientRect"); .boundingClientRect, why the backets? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:52: checkRect(expected['intersectionRect'], actual + ".intersectionRect"); ditto, why the brackets? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:54: var actual_target = eval(actual + ".target"); can we avoid this eval and just list all the right asserts instead? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:63: var max = Math.min(eval(actual + ".length"), expected.length); don't eval, the test should contain the statements, this test is pretty hard to debug, can you just inline what you expect? I know that might be verbose but it's better than generating a bunch of code like this. It tends to be super hard to debug later. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:2: <div style="width:100%;height:700px;"></div> space after ; to be consistent? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:3: <iframe id="target-iframe" src="../resources/intersection-observer-subframe.html" style="height: 100px; overflow-y: scroll" onload="runtests()"></iframe> runTests(), but just wait for onload, doesn't that block on the iframes? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:7: <script src="helper-functions.js"></script> these should always be first, before the test stuff https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:12: function runtests() { onload = function() { https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:47: 'boundingClientRect': [ 18, 118, 468, 568 ], no quotes needed https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:64: 'intersectionRect': [ 0, 0, 0, 0 ], remove quotes https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:71: checkResults(expected3, "entries", 1); these tests are super hard to debug since the asserts are all hidden https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:2: <div style="width:100%;height:700px;"></div> ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:10: <script src="helper-functions.js"></script> ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:15: function runtests() { ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:63: 'target': target ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:87: 'rootBounds' : [ 8, 312, 608, 808 ], ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html:4: <div style="width:100%; height:700px;"></div> ditto for this whole test https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/root-margin.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/root-margin.html:34: checkResults(expected0, "entries"); every time you call checkResults you pass "entries", why have that arg? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4987: m_intersectionObserverRegistry = new IntersectionObserverRegistry(m_deliverIntersectionObservationsTimer); put the timer in the registry, it doesn't need to be in the document, when dispose() happens just clear m_intersectionObserverRegistry. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4993: ASSERT(isMainThread()); no need for the main thread assert, the whole DOM is not thread safe, you don't need thread asserts https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4995: m_intersectionObserverRegistry->deliverIntersectionObservations(); remove this, move it into the registry https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1478: if (rareData->hasIntersectionObserver()) { this code should be moved into a separate object for managing observers like ElementIntersectionObserverData this should be if (ElementIntersectionObserverData* observerData = rareData->intersectionObserverData()) observerData->updateActive() or something like that, these loops and such don't belong here. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1486: for (auto& observation: rareData->intersectionObservations()) space before : this should also be moved into the controller object you hang off the rare data https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1568: if (data->hasIntersectionObserver()) { if (ElementIntersectionObserverSet* observerSet = data->intersectionObserverSet()) observerSet->deactivate(); you can pass |this| to the methods if you don't want to store it. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1572: data->deactivateAllIntersectionObservations(); this should be inside that same deactivate method, only 2 lines of code in Element.cpp https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:3609: return ensureElementRareData().createWeakPtr(this); Remove this, we shouldn't have a createWeakPtr method on Element, you can put it inside your IntersectionObserverController if needed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:3620: if (!hasRareData() || !elementRareData()->hasIntersectionObservation()) how do we get here without having observations? normally this is an assert as it should never happen. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.cpp:41: */ this comment belongs with that macro, not in ElementRareData.cpp https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:117: { move all of this code to your separate object, put it in its own file (.h and .cpp, it doesn't all need to be inline, in fact most of it shouldn't be) https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:199: OwnPtrWillBeMember<WeakPtrFactory<Element>> m_weakPointerFactory; move to controller https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:204: // and observers for which this element is root. To tell them apart, check (observer->root() == element). keep two sets instead? This doesn't seem right https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:217: PersistentWillBeMember<IntersectionObservation::HashSet> m_intersectionObservations; PersistentWillBeMember<IntersectionObserverController> or whatever you want to call it, only add a single member here, the rest of your code and data should live there. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:19: , m_target(target->createWeakPtr()) your controller should keep the factory, not the element https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:68: rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->absoluteContentBox()); this method is so big, you might want to move some code into separate static methods so it can read like a series of computations with the big blocks out of line https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:86: void IntersectionObservation::computeIntersectionObservations(int timestamp) timestamps are doubles on the web, what is this int? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:94: if (!targetLayoutObject || (!targetLayoutObject->isBox() && !targetLayoutObject->isInline())) break your early returns into multiple statements. if (!targetLayoutObject) return; if (!targetLayoutObject->isBox() && !targetLayoutObject->isInline()) return; https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:98: if (!computeGeometry(targetRect, rootRect, intersectionRect)) use a struct? having so many out params is kind of weird, but I guess the layout code loves to do this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:101: rootRect = LayoutRect(); can computeGeometry do this instead of here? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:106: return; can we move this before the intersectionArea computation to early return sooner, also can inline the computeGeometry logic in such a way to early return before doing all of the work? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:112: observer()->enqueueIntersectionObserverEntry(new IntersectionObserverEntry(timestamp / 1000., pixelSnappedIntRect(targetRect), pixelSnappedIntRect(rootRect), pixelSnappedIntRect(intersectionRect), targetElement)); .0, but timestamp should be a double too https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:25: struct ValueTypeHash { hmm, we've never had to do this before for a normal web feature, why do you want custom hash traits like this? I'd rather you didn't do that. If we do want to do this move it all to a separate header, not inside the class def, it obscures all the other code here. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:52: { yeah I'd really rather we didn't do this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:64: static typename HashSetType::iterator find(HashSetType& hashSet, IntersectionObserver* observer, Element* target) remove this, no templates or statics like this, add methods to your controller for the specific purposes https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:76: static void remove(HashSetType& hashSet, IntersectionObserver* observer, Element* target) ditto for all of these https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:81: IntersectionObservation(IntersectionObserver&, Element*, bool); bool needs an argument name, why not an enum? We don't usually do bools like this, also the Element should be a reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:93: void computeIntersectionObservations(int); int needs an argument name, primitive arguments always need variable names unless the method contains the type in it like setWidth(int) https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:104: bool m_canReportRootBounds; I'd swap these with the float, and make them both bitfields so they pack, but I guess you don't have many observers so it's not a big deal https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:26: ASSERT(isMainThread()); no need for thread checks, the DOM is not thread safe, all the code is covered in this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:32: if (context->isDocument()) { can we actually get here with an execution context that isn't a document? I don't think we can, this can just be toDocument() I think and we'll crash if the type was wrong https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:33: Frame* mainFrame = static_cast<Document*>(context)->frame()->tree().top(); toDocument, never static_cast DOM types, it misses the type checks https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:35: root = toLocalFrame(mainFrame)->document()->documentElement(); documentElement can be null, does the spec say that throws? Normally the spec would use the ICB of the document then https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:45: CSSTokenizer::Scope tokenizerScope(observerInit.rootMargin()); move this into a static (or anon namespace) parse* method https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:83: for (auto thresholdValue: thresholdParam.getAsDoubleArray()) ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:89: for (auto thresholdValue: thresholds) { space before : https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:99: return new IntersectionObserver(callback, root.get(), rootMargin, thresholds); root can't be null, so this should be *root https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:106: , m_entries(new IntersectionObserverEntryVector()) write types, why are the sets heap allocated though? just put them as members https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:109: switch (rootMargin.size()) { what happens if the size is > 4? Shouldn't we throw or assert? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:128: m_leftMargin = rootMargin[3]; we could be clever and I think you can actually express this with mod math in the index? You don't need to do that though https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:134: bool IntersectionObserver::isDescendantOfRoot(Element* target) const reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:140: if (target == rootElement || !target->inDocument() || !rootElement->inDocument()) multiple return statements https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:146: while (targetFrame != rootFrame) { why do you need this loop if you have the targetFrame->tree().isDescendantOf(rootFrame) check? What are you looking for here? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:150: target = toHTMLFrameOwnerElement(targetOwner); use ownerElement() and loop through the document ptrs, that makes this loop tiny https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:151: ASSERT(target); this isn't possible, you null checked targetOwner above, then this is just a cast, no reason to assert. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:153: if (!targetFrame) normally you'd loop through document().ownerElement() pointers, and not use frame() or need to cast like this. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:170: if (exceptionState.hadException()) we'd normally do each one as a separate block with a return. if (!m_root) { exceptionState.throwDOMException(HierarchyRequestError, ...); return; } if (...) { exceptionState.throwDOMException(...); return; } instead of checking at the end like this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:191: IntersectionObservation::WeakHashSet::iterator observationIterator = IntersectionObservation::find(*m_observations, this, target); auto for the type here, also use the controller, not these static methods. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:197: void IntersectionObserver::computeIntersectionObservations(int timestamp) double? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:202: for (auto& observation: *m_observations) space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:215: for (auto& observation: *m_observations) missing space before colon in all your loops https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:217: for (auto& observation: toDisconnect) itto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:219: ASSERT(!m_observations->size()); ->isEmpty() https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:222: IntersectionObserverEntryVector IntersectionObserver::takeRecords() put type here https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:230: void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserverEntry* entry) reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:232: ASSERT(isMainThread()); remove https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:234: static_cast<Document*>(m_callback->executionContext())->intersectionObserverRegistry()->scheduleIntersectionObserverForDelivery(*this); toDocument() https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:240: return static_cast<int>(referenceLength.toFloat() * length.percent() / 100.); .0 https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:261: while (result < max && m_thresholds[result] < ratio) no reason to cache the max on the stack like that https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:277: if (m_entries->isEmpty()) why is m_entries a pointer and not just a member? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:282: m_callback->handleEvent(entries, this); *this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:288: for (auto& observation: *m_observations) space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:303: for (auto& observation: *m_observations) space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:305: for (auto& observation: toDisconnect) space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:307: ASSERT(!m_observations->size()); isEmpty() https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:23: typedef HeapVector<Member<IntersectionObserverEntry>> IntersectionObserverEntryVector; remove the typedefs, the name is nearly as long as the actual type and just obscures the code, you can use auto in loops to avoid typing it https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:29: typedef HeapHashSet<WeakMember<IntersectionObserver>> WeakHashSet; I'd remove these and just type out the types in the right spots, this also prevents forward decls in places. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:43: bool isDescendantOfRoot(Element*) const; reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:44: void enqueueIntersectionObserverEntry(IntersectionObserverEntry*); reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:59: explicit IntersectionObserver(IntersectionObserverCallback*, Element*, const Vector<Length>&, const Vector<float>&); reference for most of these I think? Also the two vectors need argument names https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:62: Needed() not Necessary. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:65: Member<IntersectionObservation::WeakHashSet> m_observations; just write the types https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:71: Length m_leftMargin; wow these things are big... you definitely won't want an observer per widget in your page https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:24: ClientRect* boundingClientRect() { return ClientRect::create(m_boundingClientRect); } these objects need to be stable, you want to store the ClientRect objects as members and just return them, not malloc and wrap a new one every time. you should add tests for that too. assert(entry.boundingClientRect == entry.boundingClientRect) https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:25: ClientRect* rootBounds() { return ClientRect::create(m_rootBounds); } ditto https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl:10: readonly attribute ClientRect boundingClientRect; properties are always stable in IDL unless they're written with [NewObject], that means you need to cache the ClientRect objects as members https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:12: typedef HeapVector<Member<IntersectionObserver>> IntersectionObserverVector; remove https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:18: , m_suspendedIntersectionObservers(new IntersectionObserver::HashSet()) remove, just make them members https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:62: for (auto& observer: *m_trackedIntersectionObservers) space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:74: for (auto& observer: *m_trackedIntersectionObservers) { space https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:78: for (auto& observer: toRemove) space, we really need to get the linter fixed for this https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:20: class IntersectionObserverRegistry : public GarbageCollectedFinalized<IntersectionObserverRegistry> { controller or something https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:29: void removeTrackedObserversForRoot(Element*); reference https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:34: Timer<Document>& m_timer; this should live here, not in document https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:36: Member<IntersectionObserver::WeakHashSet> m_trackedIntersectionObservers; just put them as members? they don't need to be inside Member<> https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:39: Member<IntersectionObserver::HashSet> m_suspendedIntersectionObservers; ditto for these, also just write the types https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/frame/FrameView.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:3915: if (parent) { if (FrameView* parent = ...) {
I haven't had a chance to review the tests yet, but here's comments on the C++ code. Please move the rootMargin code to a followup patch. This patch is already way too huge. Anything that can be moved to followup patches really should. Is there anything other than the rootMargin code that could be extracted out into a followup patch as well? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:203: // Note that m_intersectionObservers will contain both observers for which this element is target, This comment is in the wrong place? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:30: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); Not sure this documentElement check is correct. The documentElement is actually the <html> element. There's no element that corresponds to the layoutView. So, the null root value is special in that way as well. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:98: if (!computeGeometry(targetRect, rootRect, intersectionRect)) On 2015/12/12 at 00:14:13, esprehn wrote: > use a struct? having so many out params is kind of weird, but I guess the layout code loves to do this We usually use structs for these in the layout code as well. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:110: setLastVisibleRatio(newVisibleRatio); Can we actually just store the index? We don't need the ratio for anything. That way you don't need to recompute the index for the last ratio. Also, you could get bitpack the index with the bools in the Observation. It's not an optimization that will matter, but may as well if it's free and adds no complexity. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:104: bool m_canReportRootBounds; Bikeshed nit: We typically call this m_shouldReportRootBounds. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:35: root = toLocalFrame(mainFrame)->document()->documentElement(); On 2015/12/12 at 00:14:14, esprehn wrote: > documentElement can be null, does the spec say that throws? Normally the spec would use the ICB of the document then If there's no root specified, then the root should be the Document, not the documentElement I think. Which means that they type needs to be Node, not Element. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:58: case CSSPrimitiveValue::UnitType::Number: Is this Number thing correct? In regular CSS, you can only use Number inside of quirksmode with 0 as a special value in standards mode. We should match CSS here. Is it not possible to share some of this code with the margin parser that CSS uses? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:167: exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target."); Is this not covered by the isDescendantOfRoot check? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:62: On 2015/12/12 at 00:14:16, esprehn wrote: > Needed() not Necessary. Lets just call this checkRootAndDetach. We have too many gratuitous "IfNeeded" throughout the code. It's gotten to a point where it's meaningless. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:25: m_timer.startOneShot(0, BLINK_FROM_HERE); Add a TODO to do this at idle time? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:37: for (size_t i = 0; i < suspended.size(); ++i) { use auto : ? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:50: for (size_t i = 0; i < observers.size(); ++i) { use auto : ? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/frame/FrameView.cpp (left): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:2379: updateViewportIntersectionsForSubtree(); I think removing this is the right change, but should be done in a separate patch by itself (e.g. so Sami can review it and so it doesn't get reverted if this patch does). https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/frame/FrameView.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:3913: if (m_needsUpdateViewportIntersection) { Why did you get rid of the early return?
https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:62: On 2015/12/12 at 01:06:29, ojan wrote: > On 2015/12/12 at 00:14:16, esprehn wrote: > > Needed() not Necessary. > > Lets just call this checkRootAndDetach. We have too many gratuitous "IfNeeded" throughout the code. It's gotten to a point where it's meaningless. Actually looking at this function just call it detachRoot(), if there's a root, it always "detaches", and an early return when there's no room is very sensible.
On 2015/12/12 at 01:49:52, esprehn wrote: > https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... > File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): > > https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... > third_party/WebKit/Source/core/dom/IntersectionObserver.h:62: > On 2015/12/12 at 01:06:29, ojan wrote: > > On 2015/12/12 at 00:14:16, esprehn wrote: > > > Needed() not Necessary. > > > > Lets just call this checkRootAndDetach. We have too many gratuitous "IfNeeded" throughout the code. It's gotten to a point where it's meaningless. > > Actually looking at this function just call it detachRoot(), if there's a root, it always "detaches", and an early return when there's no room is very sensible. no root*
Description was changed from ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. BUG= ========== to ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. Also why is kouhei designing some full story about first paint without talking to me.... who's been working on this for a year now? BUG= ==========
Description was changed from ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. Also why is kouhei designing some full story about first paint without talking to me.... who's been working on this for a year now? BUG= ========== to ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... ==========
Description was changed from ========== IntersectionObserver: second cut. Basic functionality is complete, it compiles, it is completely untested. https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... ========== to ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... ==========
All comments addressed, and the tests are passing again. https://codereview.chromium.org/1449623002/diff/100001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/100001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.h:547: bool computeIntersection(Element*, LayoutRect&, LayoutRect&, LayoutRect&); On 2015/12/12 00:14:12, esprehn wrote: > const, also const Element* hopefully. This method has been refactored out. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:23: "\nintersectionRect=" + rectToString(entry.intersectionRect) + On 2015/12/12 00:14:12, esprehn wrote: > I'd put each of these on a line, and put a separate "\n" so the "foo=" blocks > line up Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:39: function rectToString(rect) { On 2015/12/12 00:14:12, esprehn wrote: > defined twice in this file Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:51: checkRect(expected['boundingClientRect'], actual + ".boundingClientRect"); On 2015/12/12 00:14:12, esprehn wrote: > .boundingClientRect, why the backets? Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:52: checkRect(expected['intersectionRect'], actual + ".intersectionRect"); On 2015/12/12 00:14:12, esprehn wrote: > ditto, why the brackets? Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:54: var actual_target = eval(actual + ".target"); On 2015/12/12 00:14:12, esprehn wrote: > can we avoid this eval and just list all the right asserts instead? Added a method to js-test.js to avoid doing the eval here. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/helper-functions.js:63: var max = Math.min(eval(actual + ".length"), expected.length); On 2015/12/12 00:14:12, esprehn wrote: > don't eval, the test should contain the statements, this test is pretty hard to > debug, can you just inline what you expect? I know that might be verbose but > it's better than generating a bunch of code like this. It tends to be super hard > to debug later. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:2: <div style="width:100%;height:700px;"></div> On 2015/12/12 00:14:12, esprehn wrote: > space after ; to be consistent? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:3: <iframe id="target-iframe" src="../resources/intersection-observer-subframe.html" style="height: 100px; overflow-y: scroll" onload="runtests()"></iframe> On 2015/12/12 00:14:12, esprehn wrote: > runTests(), but just wait for onload, doesn't that block on the iframes? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:7: <script src="helper-functions.js"></script> On 2015/12/12 00:14:12, esprehn wrote: > these should always be first, before the test stuff Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:12: function runtests() { On 2015/12/12 00:14:12, esprehn wrote: > onload = function() { Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:47: 'boundingClientRect': [ 18, 118, 468, 568 ], On 2015/12/12 00:14:12, esprehn wrote: > no quotes needed I removed all of the 'expected' variables, here and elsewhere. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:64: 'intersectionRect': [ 0, 0, 0, 0 ], On 2015/12/12 00:14:12, esprehn wrote: > remove quotes Same. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-no-root.html:71: checkResults(expected3, "entries", 1); On 2015/12/12 00:14:12, esprehn wrote: > these tests are super hard to debug since the asserts are all hidden Inlined all asserts. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:2: <div style="width:100%;height:700px;"></div> On 2015/12/12 00:14:13, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:10: <script src="helper-functions.js"></script> On 2015/12/12 00:14:12, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:15: function runtests() { On 2015/12/12 00:14:12, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:63: 'target': target On 2015/12/12 00:14:12, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/iframe-root.html:87: 'rootBounds' : [ 8, 312, 608, 808 ], On 2015/12/12 00:14:12, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/multiple-thresholds.html:4: <div style="width:100%; height:700px;"></div> On 2015/12/12 00:14:13, esprehn wrote: > ditto for this whole test Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... File third_party/WebKit/LayoutTests/intersection-observer/root-margin.html (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Lay... third_party/WebKit/LayoutTests/intersection-observer/root-margin.html:34: checkResults(expected0, "entries"); On 2015/12/12 00:14:13, esprehn wrote: > every time you call checkResults you pass "entries", why have that arg? checkResults is gone now. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4987: m_intersectionObserverRegistry = new IntersectionObserverRegistry(m_deliverIntersectionObservationsTimer); On 2015/12/12 00:14:13, esprehn wrote: > put the timer in the registry, it doesn't need to be in the document, when > dispose() happens just clear m_intersectionObserverRegistry. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4993: ASSERT(isMainThread()); On 2015/12/12 00:14:13, esprehn wrote: > no need for the main thread assert, the whole DOM is not thread safe, you don't > need thread asserts Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4995: m_intersectionObserverRegistry->deliverIntersectionObservations(); On 2015/12/12 00:14:13, esprehn wrote: > remove this, move it into the registry Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1478: if (rareData->hasIntersectionObserver()) { On 2015/12/12 00:14:13, esprehn wrote: > this code should be moved into a separate object for managing observers like > ElementIntersectionObserverData > > this should be > > if (ElementIntersectionObserverData* observerData = > rareData->intersectionObserverData()) > observerData->updateActive() > > or something like that, these loops and such don't belong here. I moved all the logic into ElementRareData and the new class ElementIntersectionObserverData. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1486: for (auto& observation: rareData->intersectionObservations()) On 2015/12/12 00:14:13, esprehn wrote: > space before : > > this should also be moved into the controller object you hang off the rare data same comment https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1568: if (data->hasIntersectionObserver()) { On 2015/12/12 00:14:13, esprehn wrote: > if (ElementIntersectionObserverSet* observerSet = > data->intersectionObserverSet()) > observerSet->deactivate(); > > you can pass |this| to the methods if you don't want to store it. I moved all the logic into ElementRareData and ElementIntersectionObserverData. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1572: data->deactivateAllIntersectionObservations(); On 2015/12/12 00:14:13, esprehn wrote: > this should be inside that same deactivate method, only 2 lines of code in > Element.cpp same comment https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:3609: return ensureElementRareData().createWeakPtr(this); On 2015/12/12 00:14:13, esprehn wrote: > Remove this, we shouldn't have a createWeakPtr method on Element, you can put it > inside your IntersectionObserverController if needed. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:3620: if (!hasRareData() || !elementRareData()->hasIntersectionObservation()) On 2015/12/12 00:14:13, esprehn wrote: > how do we get here without having observations? normally this is an assert as it > should never happen. Good point, fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.cpp:41: */ On 2015/12/12 00:14:13, esprehn wrote: > this comment belongs with that macro, not in ElementRareData.cpp This was meant for a separate patch, removing it from here. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:117: { On 2015/12/12 00:14:13, esprehn wrote: > move all of this code to your separate object, put it in its own file (.h and > .cpp, it doesn't all need to be inline, in fact most of it shouldn't be) Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:199: OwnPtrWillBeMember<WeakPtrFactory<Element>> m_weakPointerFactory; On 2015/12/12 00:14:13, esprehn wrote: > move to controller Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:203: // Note that m_intersectionObservers will contain both observers for which this element is target, On 2015/12/12 01:06:28, ojan wrote: > This comment is in the wrong place? That's an obsolete comment, I removed it. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:204: // and observers for which this element is root. To tell them apart, check (observer->root() == element). On 2015/12/12 00:14:13, esprehn wrote: > keep two sets instead? This doesn't seem right same comment https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:217: PersistentWillBeMember<IntersectionObservation::HashSet> m_intersectionObservations; On 2015/12/12 00:14:13, esprehn wrote: > PersistentWillBeMember<IntersectionObserverController> or whatever you want to > call it, only add a single member here, the rest of your code and data should > live there. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:19: , m_target(target->createWeakPtr()) On 2015/12/12 00:14:13, esprehn wrote: > your controller should keep the factory, not the element Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:30: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); On 2015/12/12 01:06:28, ojan wrote: > Not sure this documentElement check is correct. The documentElement is actually > the <html> element. There's no element that corresponds to the layoutView. So, > the null root value is special in that way as well. If root is not provided in the constructor, it does this: root = toLocalFrame(mainFrame)->document()->documentElement(); This can also be set explicitly, e.g. "new IntersectionObserver(callback {root: document.documentElement()})". I don't think there's any reason to distinguish between these two cases. The reason for setting rootLayoutObject to be the layout view if rootIsDocumentElement is to make sure this code is ready for root layer scrolling. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:68: rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->absoluteContentBox()); On 2015/12/12 00:14:13, esprehn wrote: > this method is so big, you might want to move some code into separate static > methods so it can read like a series of computations with the big blocks out of > line I added helper functions, this method is now short. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:86: void IntersectionObservation::computeIntersectionObservations(int timestamp) On 2015/12/12 00:14:13, esprehn wrote: > timestamps are doubles on the web, what is this int? I think I switched types mid-way through :) I changed this to double throughout. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:94: if (!targetLayoutObject || (!targetLayoutObject->isBox() && !targetLayoutObject->isInline())) On 2015/12/12 00:14:13, esprehn wrote: > break your early returns into multiple statements. > > if (!targetLayoutObject) > return; > if (!targetLayoutObject->isBox() && !targetLayoutObject->isInline()) > return; Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:98: if (!computeGeometry(targetRect, rootRect, intersectionRect)) On 2015/12/12 01:06:28, ojan wrote: > On 2015/12/12 at 00:14:13, esprehn wrote: > > use a struct? having so many out params is kind of weird, but I guess the > layout code loves to do this > > We usually use structs for these in the layout code as well. Added struct IntersectionGeometry for this. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:101: rootRect = LayoutRect(); On 2015/12/12 00:14:13, esprehn wrote: > can computeGeometry do this instead of here? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:106: return; On 2015/12/12 00:14:13, esprehn wrote: > can we move this before the intersectionArea computation to early return sooner, > also can inline the computeGeometry logic in such a way to early return before > doing all of the work? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:110: setLastVisibleRatio(newVisibleRatio); On 2015/12/12 01:06:28, ojan wrote: > Can we actually just store the index? We don't need the ratio for anything. That > way you don't need to recompute the index for the last ratio. Also, you could > get bitpack the index with the bools in the Observation. It's not an > optimization that will matter, but may as well if it's free and adds no > complexity. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:112: observer()->enqueueIntersectionObserverEntry(new IntersectionObserverEntry(timestamp / 1000., pixelSnappedIntRect(targetRect), pixelSnappedIntRect(rootRect), pixelSnappedIntRect(intersectionRect), targetElement)); On 2015/12/12 00:14:13, esprehn wrote: > .0, but timestamp should be a double too Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:25: struct ValueTypeHash { On 2015/12/12 00:14:14, esprehn wrote: > hmm, we've never had to do this before for a normal web feature, why do you want > custom hash traits like this? I'd rather you didn't do that. > > If we do want to do this move it all to a separate header, not inside the class > def, it obscures all the other code here. Removed the custom hash_traits. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:52: { On 2015/12/12 00:14:14, esprehn wrote: > yeah I'd really rather we didn't do this Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:64: static typename HashSetType::iterator find(HashSetType& hashSet, IntersectionObserver* observer, Element* target) On 2015/12/12 00:14:14, esprehn wrote: > remove this, no templates or statics like this, add methods to your controller > for the specific purposes Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:76: static void remove(HashSetType& hashSet, IntersectionObserver* observer, Element* target) On 2015/12/12 00:14:14, esprehn wrote: > ditto for all of these Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:81: IntersectionObservation(IntersectionObserver&, Element*, bool); On 2015/12/12 00:14:14, esprehn wrote: > bool needs an argument name, why not an enum? We don't usually do bools like > this, also the Element should be a reference The bool is canReportRootBounds, is there some other idiomatic way to do this? The value is computed in IntersectionObserver::observe() because it's needed there, and I didn't think it made sense to recompute it here. Changed the Element argument to a reference. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:93: void computeIntersectionObservations(int); On 2015/12/12 00:14:14, esprehn wrote: > int needs an argument name, primitive arguments always need variable names > unless the method contains the type in it like setWidth(int) Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:104: bool m_canReportRootBounds; On 2015/12/12 01:06:28, ojan wrote: > Bikeshed nit: We typically call this m_shouldReportRootBounds. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:104: bool m_canReportRootBounds; On 2015/12/12 00:14:14, esprehn wrote: > I'd swap these with the float, and make them both bitfields so they pack, but I > guess you don't have many observers so it's not a big deal Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:26: ASSERT(isMainThread()); On 2015/12/12 00:14:14, esprehn wrote: > no need for thread checks, the DOM is not thread safe, all the code is covered > in this Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:32: if (context->isDocument()) { On 2015/12/12 00:14:14, esprehn wrote: > can we actually get here with an execution context that isn't a document? I > don't think we can, this can just be toDocument() I think and we'll crash if the > type was wrong Changed to ASSERT(context->isDocument()); https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:33: Frame* mainFrame = static_cast<Document*>(context)->frame()->tree().top(); On 2015/12/12 00:14:14, esprehn wrote: > toDocument, never static_cast DOM types, it misses the type checks Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:35: root = toLocalFrame(mainFrame)->document()->documentElement(); On 2015/12/12 01:06:29, ojan wrote: > On 2015/12/12 at 00:14:14, esprehn wrote: > > documentElement can be null, does the spec say that throws? Normally the spec > would use the ICB of the document then > > If there's no root specified, then the root should be the Document, not the > documentElement I think. Which means that they type needs to be Node, not > Element. That's going to make the code a lot grosser. Currently, the code throws an exception if the document element is null. Under what circumstances would the document element be null, and do we have to allow observers to be created in those circumstances? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:45: CSSTokenizer::Scope tokenizerScope(observerInit.rootMargin()); On 2015/12/12 00:14:16, esprehn wrote: > move this into a static (or anon namespace) parse* method Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:58: case CSSPrimitiveValue::UnitType::Number: On 2015/12/12 01:06:29, ojan wrote: > Is this Number thing correct? In regular CSS, you can only use Number inside of > quirksmode with 0 as a special value in standards mode. We should match CSS > here. > > Is it not possible to share some of this code with the margin parser that CSS > uses? This is very unfamiliar territory for me, but the guidance I received was to take this approach. I removed the NumberToken and UnitType::Number cases. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:83: for (auto thresholdValue: thresholdParam.getAsDoubleArray()) On 2015/12/12 00:14:15, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:89: for (auto thresholdValue: thresholds) { On 2015/12/12 00:14:15, esprehn wrote: > space before : Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:99: return new IntersectionObserver(callback, root.get(), rootMargin, thresholds); On 2015/12/12 00:14:15, esprehn wrote: > root can't be null, so this should be *root Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:106: , m_entries(new IntersectionObserverEntryVector()) On 2015/12/12 00:14:14, esprehn wrote: > write types, why are the sets heap allocated though? just put them as members Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:109: switch (rootMargin.size()) { On 2015/12/12 00:14:15, esprehn wrote: > what happens if the size is > 4? Shouldn't we throw or assert? I figured we would just ignore extra junk at the end, is it better to throw? https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:128: m_leftMargin = rootMargin[3]; On 2015/12/12 00:14:15, esprehn wrote: > we could be clever and I think you can actually express this with mod math in > the index? You don't need to do that though Acknowledged. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:134: bool IntersectionObserver::isDescendantOfRoot(Element* target) const On 2015/12/12 00:14:14, esprehn wrote: > reference Same comment as header: since this method assigns to target and null-checks it, I would argue it makes more sense to leave it as a pointer. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:140: if (target == rootElement || !target->inDocument() || !rootElement->inDocument()) On 2015/12/12 00:14:15, esprehn wrote: > multiple return statements Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:146: while (targetFrame != rootFrame) { On 2015/12/12 00:14:14, esprehn wrote: > why do you need this loop if you have the > targetFrame->tree().isDescendantOf(rootFrame) check? What are you looking for > here? I need to make sure that the root element is an ancestor of the iframe element whose document contains the target. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:150: target = toHTMLFrameOwnerElement(targetOwner); On 2015/12/12 00:14:15, esprehn wrote: > use ownerElement() and loop through the document ptrs, that makes this loop tiny Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:151: ASSERT(target); On 2015/12/12 00:14:14, esprehn wrote: > this isn't possible, you null checked targetOwner above, then this is just a > cast, no reason to assert. Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:153: if (!targetFrame) On 2015/12/12 00:14:15, esprehn wrote: > normally you'd loop through document().ownerElement() pointers, and not use > frame() or need to cast like this. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:167: exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target."); On 2015/12/12 01:06:29, ojan wrote: > Is this not covered by the isDescendantOfRoot check? I thought it might be nice to call this error out specifically, but I don't feel strongly about it. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:170: if (exceptionState.hadException()) On 2015/12/12 00:14:15, esprehn wrote: > we'd normally do each one as a separate block with a return. > > if (!m_root) { > exceptionState.throwDOMException(HierarchyRequestError, ...); > return; > } > if (...) { > exceptionState.throwDOMException(...); > return; > } > > instead of checking at the end like this Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:191: IntersectionObservation::WeakHashSet::iterator observationIterator = IntersectionObservation::find(*m_observations, this, target); On 2015/12/12 00:14:15, esprehn wrote: > auto for the type here, also use the controller, not these static methods. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:197: void IntersectionObserver::computeIntersectionObservations(int timestamp) On 2015/12/12 00:14:15, esprehn wrote: > double? Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:202: for (auto& observation: *m_observations) On 2015/12/12 00:14:14, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:215: for (auto& observation: *m_observations) On 2015/12/12 00:14:16, esprehn wrote: > missing space before colon in all your loops Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:217: for (auto& observation: toDisconnect) On 2015/12/12 00:14:14, esprehn wrote: > itto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:219: ASSERT(!m_observations->size()); On 2015/12/12 00:14:14, esprehn wrote: > ->isEmpty() Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:222: IntersectionObserverEntryVector IntersectionObserver::takeRecords() On 2015/12/12 00:14:14, esprehn wrote: > put type here Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:230: void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserverEntry* entry) On 2015/12/12 00:14:14, esprehn wrote: > reference Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:232: ASSERT(isMainThread()); On 2015/12/12 00:14:14, esprehn wrote: > remove Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:234: static_cast<Document*>(m_callback->executionContext())->intersectionObserverRegistry()->scheduleIntersectionObserverForDelivery(*this); On 2015/12/12 00:14:14, esprehn wrote: > toDocument() Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:240: return static_cast<int>(referenceLength.toFloat() * length.percent() / 100.); On 2015/12/12 00:14:15, esprehn wrote: > .0 Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:261: while (result < max && m_thresholds[result] < ratio) On 2015/12/12 00:14:14, esprehn wrote: > no reason to cache the max on the stack like that Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:277: if (m_entries->isEmpty()) On 2015/12/12 00:14:14, esprehn wrote: > why is m_entries a pointer and not just a member? Fixed. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:282: m_callback->handleEvent(entries, this); On 2015/12/12 00:14:14, esprehn wrote: > *this Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:288: for (auto& observation: *m_observations) On 2015/12/12 00:14:14, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:303: for (auto& observation: *m_observations) On 2015/12/12 00:14:15, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:305: for (auto& observation: toDisconnect) On 2015/12/12 00:14:15, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:307: ASSERT(!m_observations->size()); On 2015/12/12 00:14:14, esprehn wrote: > isEmpty() Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:23: typedef HeapVector<Member<IntersectionObserverEntry>> IntersectionObserverEntryVector; On 2015/12/12 00:14:16, esprehn wrote: > remove the typedefs, the name is nearly as long as the actual type and just > obscures the code, you can use auto in loops to avoid typing it Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:29: typedef HeapHashSet<WeakMember<IntersectionObserver>> WeakHashSet; On 2015/12/12 00:14:16, esprehn wrote: > I'd remove these and just type out the types in the right spots, this also > prevents forward decls in places. Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:43: bool isDescendantOfRoot(Element*) const; On 2015/12/12 00:14:16, esprehn wrote: > reference I'd argue that in this particular case, using a reference doesn't make sense. The body of the method reassigns to the Element* parameter as it walks up the frame tree, so the method is already doing null checks. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:44: void enqueueIntersectionObserverEntry(IntersectionObserverEntry*); On 2015/12/12 00:14:16, esprehn wrote: > reference Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:59: explicit IntersectionObserver(IntersectionObserverCallback*, Element*, const Vector<Length>&, const Vector<float>&); On 2015/12/12 00:14:16, esprehn wrote: > reference for most of these I think? Also the two vectors need argument names Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:62: On 2015/12/12 01:49:52, esprehn wrote: > On 2015/12/12 at 01:06:29, ojan wrote: > > On 2015/12/12 at 00:14:16, esprehn wrote: > > > Needed() not Necessary. > > > > Lets just call this checkRootAndDetach. We have too many gratuitous "IfNeeded" > throughout the code. It's gotten to a point where it's meaningless. > > Actually looking at this function just call it detachRoot(), if there's a root, > it always "detaches", and an early return when there's no room is very sensible. That's not actually what it does. Rather, it checks to see if the root has been gc'ed, and if so, it basically immolates the observer and -- critically -- releases its reference to the callback execution context. I did s/Necessary/Needed/, but I didn't rename it to detachRoot, because I think that's misleading. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:65: Member<IntersectionObservation::WeakHashSet> m_observations; On 2015/12/12 00:14:16, esprehn wrote: > just write the types Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:71: Length m_leftMargin; On 2015/12/12 00:14:16, esprehn wrote: > wow these things are big... you definitely won't want an observer per widget in > your page That would be a very strange use case. I agree that IntersectionObserver can be added to the long list of "things you wouldn't want to do for every widget in your page." https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:24: ClientRect* boundingClientRect() { return ClientRect::create(m_boundingClientRect); } On 2015/12/12 00:14:16, esprehn wrote: > these objects need to be stable, you want to store the ClientRect objects as > members and just return them, not malloc and wrap a new one every time. > > you should add tests for that too. > > assert(entry.boundingClientRect == entry.boundingClientRect) Done, and added this check to LayoutTests/intersection-observer/same-document-no-root.hml https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:25: ClientRect* rootBounds() { return ClientRect::create(m_rootBounds); } On 2015/12/12 00:14:16, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.idl:10: readonly attribute ClientRect boundingClientRect; On 2015/12/12 00:14:16, esprehn wrote: > properties are always stable in IDL unless they're written with [NewObject], > that means you need to cache the ClientRect objects as members Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:12: typedef HeapVector<Member<IntersectionObserver>> IntersectionObserverVector; On 2015/12/12 00:14:16, esprehn wrote: > remove Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:18: , m_suspendedIntersectionObservers(new IntersectionObserver::HashSet()) On 2015/12/12 00:14:16, esprehn wrote: > remove, just make them members Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:25: m_timer.startOneShot(0, BLINK_FROM_HERE); On 2015/12/12 01:06:29, ojan wrote: > Add a TODO to do this at idle time? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:37: for (size_t i = 0; i < suspended.size(); ++i) { On 2015/12/12 01:06:29, ojan wrote: > use auto : ? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:50: for (size_t i = 0; i < observers.size(); ++i) { On 2015/12/12 01:06:29, ojan wrote: > use auto : ? Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:62: for (auto& observer: *m_trackedIntersectionObservers) On 2015/12/12 00:14:16, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:74: for (auto& observer: *m_trackedIntersectionObservers) { On 2015/12/12 00:14:16, esprehn wrote: > space Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.cpp:78: for (auto& observer: toRemove) On 2015/12/12 00:14:16, esprehn wrote: > space, we really need to get the linter fixed for this Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:20: class IntersectionObserverRegistry : public GarbageCollectedFinalized<IntersectionObserverRegistry> { On 2015/12/12 00:14:16, esprehn wrote: > controller or something Renamed to IntersectionObserverController. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:29: void removeTrackedObserversForRoot(Element*); On 2015/12/12 00:14:16, esprehn wrote: > reference Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:34: Timer<Document>& m_timer; On 2015/12/12 00:14:16, esprehn wrote: > this should live here, not in document Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:36: Member<IntersectionObserver::WeakHashSet> m_trackedIntersectionObservers; On 2015/12/12 00:14:16, esprehn wrote: > just put them as members? they don't need to be inside Member<> Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverRegistry.h:39: Member<IntersectionObserver::HashSet> m_suspendedIntersectionObservers; On 2015/12/12 00:14:16, esprehn wrote: > ditto for these, also just write the types Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/frame/FrameView.cpp (left): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:2379: updateViewportIntersectionsForSubtree(); On 2015/12/12 01:06:29, ojan wrote: > I think removing this is the right change, but should be done in a separate > patch by itself (e.g. so Sami can review it and so it doesn't get reverted if > this patch does). Done. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/frame/FrameView.cpp (right): https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:3913: if (m_needsUpdateViewportIntersection) { On 2015/12/12 01:06:29, ojan wrote: > Why did you get rid of the early return? I can't remember why I did that, but it's not needed, so I reverted to the old code. https://codereview.chromium.org/1449623002/diff/160001/third_party/WebKit/Sou... third_party/WebKit/Source/core/frame/FrameView.cpp:3915: if (parent) { On 2015/12/12 00:14:16, esprehn wrote: > if (FrameView* parent = ...) { Since I reverted to the old code, the parent variable is needed outside the scope of the 'if' statement.
I think there's some confusion here, if passing no root is the same as passing documentElement, we should just have people pass documentElement. I thought the purpose of the null root was that you got magic behavior that used the LayoutView. For example I'd expect: var target = document.getElementById("target"); var observer = new IntersectionObserver(function(records) { }); observer.observe(target); document.documentElement.remove(); document.appendChild(document.createElement("div")).innerHTML = "lots of new content"; document.documentElement.appendChild(target); I would not expect this to make the observer stop working, it has no root specified, so it should use whatever documentElement is available, not the one you specified when you created it.
On 2015/12/16 20:58:32, esprehn wrote: > I think there's some confusion here, if passing no root is the same as passing > documentElement, we should just have people pass documentElement. I thought the > purpose of the null root was that you got magic behavior that used the > LayoutView. > > For example I'd expect: > > var target = document.getElementById("target"); > var observer = new IntersectionObserver(function(records) { }); > observer.observe(target); > document.documentElement.remove(); > document.appendChild(document.createElement("div")).innerHTML = "lots of new > content"; > document.documentElement.appendChild(target); > > I would not expect this to make the observer stop working, it has no root > specified, so it should use whatever documentElement is available, not the one > you specified when you created it. I see your point, and the fix for this will be non-trivial. Given that this CL is a month old, and we have customers eagerly awaiting a chance to try it out, I'd prefer to fix it in another patch. WDYT?
On 2015/12/16 at 21:42:26, szager wrote: > On 2015/12/16 20:58:32, esprehn wrote: > > I think there's some confusion here, if passing no root is the same as passing > > documentElement, we should just have people pass documentElement. I thought the > > purpose of the null root was that you got magic behavior that used the > > LayoutView. > > > > ... > > > > I would not expect this to make the observer stop working, it has no root > > specified, so it should use whatever documentElement is available, not the one > > you specified when you created it. > > I see your point, and the fix for this will be non-trivial. Given that this CL is a month old, and we have customers eagerly awaiting a chance to try it out, I'd prefer to fix it in another patch. WDYT? Sure, please file a bug about it and block the main bug on it, we probably need this fixed before we ship to stable, but it's fine for landing the code behind the flag.
Uploaded one more patch set to address lifetime issues since the code straddles the ref-counted DOM world and the garbage collected world. I have nothing more to add here, waiting for comments and lgtm.
Description was changed from ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... ========== to ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... BUG=540528 ==========
On 2015/12/16 22:12:01, esprehn wrote: > On 2015/12/16 at 21:42:26, szager wrote: > > On 2015/12/16 20:58:32, esprehn wrote: > > > I think there's some confusion here, if passing no root is the same as > passing > > > documentElement, we should just have people pass documentElement. I thought > the > > > purpose of the null root was that you got magic behavior that used the > > > LayoutView. > > > > > > ... > > > > > > I would not expect this to make the observer stop working, it has no root > > > specified, so it should use whatever documentElement is available, not the > one > > > you specified when you created it. > > > > I see your point, and the fix for this will be non-trivial. Given that this > CL is a month old, and we have customers eagerly awaiting a chance to try it > out, I'd prefer to fix it in another patch. WDYT? > > Sure, please file a bug about it and block the main bug on it, we probably need > this fixed before we ship to stable, but it's fine for landing the code behind > the flag. Filed crbug.com/570538 for this.
Did you ever get a chance to explore removing the usage of WeakPtrFactory, or is that going to be something in a followup CL?
On 2015/12/17 01:00:15, dcheng wrote: > Did you ever get a chance to explore removing the usage of WeakPtrFactory, or is > that going to be something in a followup CL? WeakPtrFactory is still necessary, even with the change you proposed. It will go away when oilpan is rolled out, which will probably happen before this feature drops its flag. I am still investigating how to do the bookkeeping you suggested. The latest patch actually introduces a dispose() method which will do accurate and synchronous bookkeeping in the ref-counted world. It's only when oilpan ships that the potential leak will be exposed.
https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4986: IntersectionObserverController* Document::intersectionObserverController() We usually make these return a reference since it always returns a value https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:2617: ElementIntersectionObserverData& Element::intersectionObserverData() ensureIntersectionObserverData() https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:2619: return ensureElementRareData().intersectionObserverData(); ensure* for the method on ElementRareData too https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:26: return m_intersectionObservations.find(&observer) != m_intersectionObservations.end(); .contains(), no need to compare to end() https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: RawPtr<IntersectionObserver> key(&observer); do these not implicitly construct? https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:37: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>>::iterator observationIterator = m_intersectionObservations.find(&observer); auto https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:38: if (observationIterator != m_intersectionObservations.end()) you can just write "it" for the iterator if you want, but you don't need to https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:44: IntersectionObserverController* registry = element.document().intersectionObserverController(); controller https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:57: document.intersectionObserverController()->removeTrackedObserversForRoot(element); maybe do this out of the loop and save to a variable like in activate*() ? https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:8: #include "core/dom/IntersectionObservation.h" you can forward declare IntersectionObservation if you put the constructor/destructor out of line https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:9: #include "core/dom/IntersectionObserver.h" ditto https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:16: ElementIntersectionObserverData() { } can we put both the constructor and the destructor out of line? This is putting all the code for the HashMap and HashSet below inline. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.cpp:34: #include "core/dom/Document.h" still needed? https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:129: ElementIntersectionObserverData& intersectionObserverData() ensure https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:21: , m_active(1) true https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:22: , m_shouldReportRootBounds(shouldReportRootBounds ? 1 : 0) ditto, just assign https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:57: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); I would just inline this into the if () below, the variable is just saying what the if is doing which is pretty clear https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:102: // Map targetRect into document coordinates. soooooo much better as steps, great! https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:155: m_target->intersectionObserverData().removeObservation(*this->observer()); if you clear the sets in the dispose method you can avoid all the hash lookups inside here, but that's a small optimization you can do later https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:31: void setActive(bool active) { m_active = active ? 1 : 0; } no need for the ternary, you can just assign the bool to the bitfield https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:81: root = toLocalFrame(mainFrame)->document()->documentElement(); Add a TODO and maybe link to that new bug? https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:125: default: case 4: and the default should ASSERT_NOT_REACHED() ? https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:130: break; I feel like we should be able to ASSERT_NOT_REACHED() for the case of > 4, since there's no way the CSS parser should allow that. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:138: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); break this apart. if (rootElement == rootElement->document().documentElement()) return rootElement->document().layoutView(); return rootElement->layoutObject(); https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:177: exceptionState.throwDOMException(HierarchyRequestError, "Observed element must be a descendant of the observer's root element."); I do kind of wonder if we should relax this in the spec, this means you can't observe a widget before you insert it which seems a little silly. We can sort that out after this lands though. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:199: // TODO: unobserve callback TODO(szager): It's good to attribute your TODO's now https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:298: return m_topMargin.type() == Percent || m_rightMargin.type() == Percent || m_bottomMargin.type() == Percent || m_leftMargin.type() == Percent; I'd wrap each || so this is multiple lines https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:309: void IntersectionObserver::checkRootAndDetachIfNeeded() hmm, I think I need to understand why you need to check this all the time in each method? This is because you don't have a callback when the weak handle goes away? I wonder if we can fix this in Oilpan, having to do all this manual checking is sad https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:23: typedef HeapVector<Member<IntersectionObserverEntry>> IntersectionObserverEntryVector; not used https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:21: // TODO: use idle callback with a timeout ditto for adding your name https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:58: // TODO: Need to define timestamp. ditto https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:22: IntersectionObserverController(); also destructor, and put it in the .cpp file https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:20: IntersectionObserverEntry(double, const IntRect&, const IntRect&, const IntRect&, Element*); need argument names for all double and IntRect ones https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:23: double time() { return m_time; } const for all these methods, certainly for the time() one
https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:4986: IntersectionObserverController* Document::intersectionObserverController() On 2015/12/17 01:40:28, esprehn wrote: > We usually make these return a reference since it always returns a value Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:2617: ElementIntersectionObserverData& Element::intersectionObserverData() On 2015/12/17 01:40:28, esprehn wrote: > ensureIntersectionObserverData() Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:2619: return ensureElementRareData().intersectionObserverData(); On 2015/12/17 01:40:28, esprehn wrote: > ensure* for the method on ElementRareData too Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:26: return m_intersectionObservations.find(&observer) != m_intersectionObservations.end(); On 2015/12/17 01:40:28, esprehn wrote: > .contains(), no need to compare to end() Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: RawPtr<IntersectionObserver> key(&observer); On 2015/12/17 01:40:28, esprehn wrote: > do these not implicitly construct? Whoops, this was left over from a previous attempt, I removed it. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:37: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>>::iterator observationIterator = m_intersectionObservations.find(&observer); On 2015/12/17 01:40:28, esprehn wrote: > auto Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:38: if (observationIterator != m_intersectionObservations.end()) On 2015/12/17 01:40:28, esprehn wrote: > you can just write "it" for the iterator if you want, but you don't need to Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:44: IntersectionObserverController* registry = element.document().intersectionObserverController(); On 2015/12/17 01:40:28, esprehn wrote: > controller Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:57: document.intersectionObserverController()->removeTrackedObserversForRoot(element); On 2015/12/17 01:40:28, esprehn wrote: > maybe do this out of the loop and save to a variable like in activate*() ? Oh, this is wrong, removeTrackedObserversForRoot should be called outside the loop. Fixed. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:8: #include "core/dom/IntersectionObservation.h" On 2015/12/17 01:40:28, esprehn wrote: > you can forward declare IntersectionObservation if you put the > constructor/destructor out of line Yep, it works, done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:9: #include "core/dom/IntersectionObserver.h" On 2015/12/17 01:40:28, esprehn wrote: > ditto Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:16: ElementIntersectionObserverData() { } On 2015/12/17 01:40:28, esprehn wrote: > can we put both the constructor and the destructor out of line? > > This is putting all the code for the HashMap and HashSet below inline. Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.cpp:34: #include "core/dom/Document.h" On 2015/12/17 01:40:28, esprehn wrote: > still needed? Nope, removed it. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:129: ElementIntersectionObserverData& intersectionObserverData() On 2015/12/17 01:40:28, esprehn wrote: > ensure Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:21: , m_active(1) On 2015/12/17 01:40:28, esprehn wrote: > true Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:22: , m_shouldReportRootBounds(shouldReportRootBounds ? 1 : 0) On 2015/12/17 01:40:28, esprehn wrote: > ditto, just assign Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:57: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); On 2015/12/17 01:40:28, esprehn wrote: > I would just inline this into the if () below, the variable is just saying what > the if is doing which is pretty clear Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:155: m_target->intersectionObserverData().removeObservation(*this->observer()); On 2015/12/17 01:40:28, esprehn wrote: > if you clear the sets in the dispose method you can avoid all the hash lookups > inside here, but that's a small optimization you can do later Acknowledged, added a TODO in IntersectionObserver::checkRootAndDetachIfNeeded. For the time being, it has an ASSERT(m_observations.isEmpty()) after all observations have been disconnected, and I'd like to leave that in for a while to see if it ever fails. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:31: void setActive(bool active) { m_active = active ? 1 : 0; } On 2015/12/17 01:40:28, esprehn wrote: > no need for the ternary, you can just assign the bool to the bitfield Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:81: root = toLocalFrame(mainFrame)->document()->documentElement(); On 2015/12/17 01:40:28, esprehn wrote: > Add a TODO and maybe link to that new bug? Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:125: default: On 2015/12/17 01:40:28, esprehn wrote: > case 4: > > and the default should ASSERT_NOT_REACHED() ? Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:130: break; On 2015/12/17 01:40:28, esprehn wrote: > I feel like we should be able to ASSERT_NOT_REACHED() for the case of > 4, since > there's no way the CSS parser should allow that. Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:138: bool rootIsDocumentElement = (rootElement == rootElement->document().documentElement()); On 2015/12/17 01:40:28, esprehn wrote: > break this apart. > > if (rootElement == rootElement->document().documentElement()) > return rootElement->document().layoutView(); > return rootElement->layoutObject(); Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:177: exceptionState.throwDOMException(HierarchyRequestError, "Observed element must be a descendant of the observer's root element."); On 2015/12/17 01:40:29, esprehn wrote: > I do kind of wonder if we should relax this in the spec, this means you can't > observe a widget before you insert it which seems a little silly. We can sort > that out after this lands though. Acknowledged. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:199: // TODO: unobserve callback On 2015/12/17 01:40:28, esprehn wrote: > TODO(szager): > > It's good to attribute your TODO's now Done, here and elsewhere. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:298: return m_topMargin.type() == Percent || m_rightMargin.type() == Percent || m_bottomMargin.type() == Percent || m_leftMargin.type() == Percent; On 2015/12/17 01:40:29, esprehn wrote: > I'd wrap each || so this is multiple lines Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:309: void IntersectionObserver::checkRootAndDetachIfNeeded() On 2015/12/17 01:40:29, esprehn wrote: > hmm, I think I need to understand why you need to check this all the time in > each method? This is because you don't have a callback when the weak handle goes > away? > > I wonder if we can fix this in Oilpan, having to do all this manual checking is > sad Now that there's ElementIntersectionObserverData::dispose(), I can "#if ENABLE(OILPAN)" the contents of this method out for now (done). Once oilpan ships, dcheng's suggestion (or haraken's suggestion in the notes to use registerWeakMemers) might work: https://docs.google.com/a/chromium.org/document/d/1gmIi87dPJ0zu6XU7Jk2t8JS6b-... I have to think about this a bit more, fortunately I have some time before oilpan ships. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:23: typedef HeapVector<Member<IntersectionObserverEntry>> IntersectionObserverEntryVector; On 2015/12/17 01:40:29, esprehn wrote: > not used Removed. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:21: // TODO: use idle callback with a timeout On 2015/12/17 01:40:29, esprehn wrote: > ditto for adding your name Done, here and elsewhere. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:22: IntersectionObserverController(); On 2015/12/17 01:40:29, esprehn wrote: > also destructor, and put it in the .cpp file Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:20: IntersectionObserverEntry(double, const IntRect&, const IntRect&, const IntRect&, Element*); On 2015/12/17 01:40:29, esprehn wrote: > need argument names for all double and IntRect ones Done. https://codereview.chromium.org/1449623002/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:23: double time() { return m_time; } On 2015/12/17 01:40:29, esprehn wrote: > const for all these methods, certainly for the time() one Done.
ping
This is an amazingly complicated but exciting CL :) It looks like we need a bit more work to clean up the object lifetime relationship. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h:24: void handleEvent(HeapVector<Member<IntersectionObserverEntry>>&, IntersectionObserver&) override; const HeapVector<>& https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp:45: IntersectionObserver* observer = IntersectionObserver::create(intersectionObserverInit, *callback, exceptionState); I think you can just pass callback instead of *callback. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp:47: V8ThrowException::throwGeneralError(info.GetIsolate(), exceptionState.message()); Why do you need this GeneralError? When writing custom bindings, please copy & paste auto-generated code and modify only the part you really need to modify. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:615: } I think we should call these in Document::detach, not Document::dispose. Remember that Document::dispose is not called in oilpan. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5073: IntersectionObserverController& Document::intersectionObserverController() intersectionObserverController() => ensureIntersectionObserverController() https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1463: rareData->ensureIntersectionObserverData().activateValidIntersectionObservers(*this); It looks a bit strange that you call ensureXXX after checking hasXXX. Shall we change the signatures as follows? IntersectionObserverData* intersectionObserverData(); // Returns nullptr if there is no IntersectionObserverData. IntersectionObserverData& ensureIntersectionObserverData(); Then you can write: if (intersectionObserverData()) intersectionObserverData()->...; https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1544: data->ensureIntersectionObserverData().deactivateAllIntersectionObservers(*this); Ditto. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: m_intersectionObservations.set(observation.observer(), &observation); Add ASSERT(!m_intersectionObservations.contains(...)); https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:42: if (it != m_intersectionObservations.end()) Can we change this to: ASSERT(it != m_intersectionObservations.end()); ? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:57: void ElementIntersectionObserverData::deactivateAllIntersectionObservers(Element& element) Just to confirm: If the Element and the Document die together, Element::removedFrom is not called. This means that deactivateAllIntersectionObservers is not called. This is not an issue, right? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:37: // IntersectionObservers for which the Element owning this data is root. Help me understand: Is it possible that a non-Document Element becomes a root of an IntersectionObserver? I was assuming that a target can be any Element but a root is a Document. Another question is: why do we need both ElementIntersectionObserverData::m_intersectionObservers and IntersectionObseverController::m_trackedObservers? I don't understand why we need to keep track of a hash set of weak members in two places. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; I don't fully understand why we need this mapping. Wouldn't it be more straightforward if we have the following two HashSets? - ElementIntersectionObserverData retains HashSet<Member<IntersectionObserver>>. - IntersectionObserver retains HashSet<Member<IntersectionObservation>>. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:43: OwnPtrWillBeMember<WeakPtrFactory<Element>> m_weakPointerFactory; This can simply be: WeakPtrFactory<Element> OwnPtrWillBeMember is not needed. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:24: target.ensureIntersectionObserverData().addObservation(*this); The logic around here is messy... - IntersectionObserver::m_observations.add is called by IntersectionObserver. - IntersectionObserver::m_observations.remove is called by IntersectionObservation::disconnect. - ElementIntersectionObserverData::m_intersectionObservations.add is called by IntersectionObservation's constructor. - ElementIntersectionObserverData::m_intersectionObservations.remove is called by IntersectionObservation::disconnect. Can we refactor the code as follows? - IntersectionObservation's constructor calls ElementIntersectionObserverData::addObservation. It calls IntersectionObserver::m_observations.add and ElementIntersectionObserverData::m_intersectionObservations.add. - IntersectionObservation::disconnect calls ElementIntersectionObserverData::removeObservation. It calls IntersectionObserver::m_observations.remove and ElementIntersectionObserverData::m_intersectionObservations.remove. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:153: m_target->ensureIntersectionObserverData().removeObservation(*this->observer()); I think you should clear m_target here. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:18: IntersectionObservation(IntersectionObserver&, Element&, bool shouldReportRootBounds); Can we add a destructor and add ASSERT(!m_target)? We need to make sure that disconnect() has been called before the IntersectionObserver gets destructed. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:48: WeakPtrWillBeWeakMember<Element> m_target; In oilpan, why does this need to be a WeakMember? Given that the IntersectionObservation should not outlive the Element, it should be a Member. In non-oilpan, I guess this doesn't need to be a WeakPtr. We can make it a RawPtr and clear it in disconnect(). I'm assuming that disconnect() is always called before the IntersectionObservation is destructed, right? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:29: while (rootMargin.size() < 5 && tokenRange.peek().type() != EOFToken && !exceptionState.hadException()) { Where does 5 come from? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:146: bool IntersectionObserver::isDescendantOfRoot(const Element* target) const It would be helpful to have a comment to explain what the "root" means. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:216: void IntersectionObserver::disconnect(IntersectionObservation& observation) Nit: We can just pass IntersectionObservation* instead of IntersectionObservation&. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:218: m_observations.remove(&observation); Add ASSERT(m_observations.contains(observation)). https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:319: // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take I'm concerned about this but will comment on it after other lifetime issues are resolved :) https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:65: WeakPtrWillBeWeakMember<Element> m_root; If you use a WeakMember, isn't there any risk that Oilpan's GC behavior becomes observable to JS? The timing when the WeakMember is cleared depends on when Oilpan's GC runs. It is not a good idea that the behavior is exposed to JS. When should the m_root become nullptr? That must be explicitly defined without relying on a GC timing. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:66: HeapHashSet<WeakMember<IntersectionObservation>> m_observations; I'm wondering why this needs to be weak. You're already adding HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> to ElementIntersectionObserverData. Doesn't it mean that you're already adding a strong reference from the IntersectionObserver to the IntersectionObservation? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.idl (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.idl:5: // http://w3c.github.io/performance-timeline/#idl-def-PerformanceObserverCallback The spec link is wrong. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:11: typedef HeapVector<Member<IntersectionObserver>> IntersectionObserverVector; Define this in the header file and use it in the header file as well. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:78: m_trackedIntersectionObservers.remove(observer); You can use removeAll. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:37: // IntersectionObservers for which this is the tracking document. Can you add a link to the dcheng's document (https://docs.google.com/document/d/1gmIi87dPJ0zu6XU7Jk2t8JS6b-Pig7gbkwZThxZjz...), or add a good comment about the lifetime relationship of the IntersectionObserver-related objects somewhere? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:38: HeapHashSet<WeakMember<IntersectionObserver>> m_trackedIntersectionObservers; What's the relationship between m_trackedIntersectionObservers, m_activeIntersectionObservers and m_suspendedIntersectionObservers? If x is in m_trackedIntersectionObservers, is it guaranteed that x is in either of m_activeIntersectionObservers or m_suspendedIntersectionObservers? If so, I'm wondering why m_trackedIntersectionObservers needs to be weak members. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:36: RefPtrWillBeMember<Element> m_target; In non-oilpan, why doesn't this RefPtr create a cycle? IntersectionObserver ==(Member)=> IntersectionObserverEntry ==(RefPtr)=> Element ==(RefPtr)=> Document ==(Persistent)=> IntersectionObserverController ==(Member)=> IntersectionObserver I guess this should be a RawPtr and cleared when the Element gets destructed. In oilpan, the cycle is totally fine. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl:5: // Spec: http://dom.spec.whatwg.org/#idl-def-IntersectionObserverInit This link doesn't exist.
I'd suggest writing a diagram of the lifetime relationship of the following objects (in both oilpan and non-oilpan) before updating the CL. - Document - Element - ElementIntersectionObserverData - IntersectionObserver - IntersectionObserverController - IntersectionObservation - IntersectionObverserEntry - IntersectionObserverCallback
This patch is big, I might have missed some things. I think you and haraken@ might benefit from a VC to discuss the design and the lifetimes though. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp:45: IntersectionObserver* observer = IntersectionObserver::create(intersectionObserverInit, *callback, exceptionState); On 2015/12/22 at 01:13:29, haraken wrote: > I think you can just pass callback instead of *callback. *callback signifies that it's not null to the ::create() method. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1544: data->ensureIntersectionObserverData().deactivateAllIntersectionObservers(*this); On 2015/12/22 at 01:13:29, haraken wrote: > Ditto. +1 https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.h:557: bool hasIntersectionObserverData() const; Yeah, if you make this be two methods: * intersectionObserverData(); & ensureIntersectionObserverData(); then callers pick the "could be missing" or "always have one, create if you must" paths. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:24: bool ElementIntersectionObserverData::hasIntersectionObservation() const I might rename these .hasObservation() and hasObserver(), no need to restate the type of the thing so many times if you don't want https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: m_intersectionObservations.set(observation.observer(), &observation); On 2015/12/22 at 01:13:29, haraken wrote: > Add ASSERT(!m_intersectionObservations.contains(...)); Since this is a HashMap this should be .add() not .set() I think? No reason to check .contains() https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:57: void ElementIntersectionObserverData::deactivateAllIntersectionObservers(Element& element) On 2015/12/22 at 01:13:29, haraken wrote: > Just to confirm: If the Element and the Document die together, Element::removedFrom is not called. This means that deactivateAllIntersectionObservers is not called. This is not an issue, right? That's only true when Oilpan is enabled I think, otherwise we always call removedDetachedChildren. Are we sure that all the clean up in removedFrom is really okay to skip with Oilpan? (in general) https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:88: return element; does this actually work? The return type doesn't need to be something like WeakPtrWillBeRawPtr? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:37: // IntersectionObservers for which the Element owning this data is root. On 2015/12/22 at 01:13:29, haraken wrote: > Help me understand: Is it possible that a non-Document Element becomes a root of an IntersectionObserver? I was assuming that a target can be any Element but a root is a Document. > root can be any element that's an ancestor. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementRareData.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementRareData.h:128: bool hasIntersectionObserverData() const { return m_intersectionObserverData; } Have a * foo() and a & ensureFoo() methods. no has() https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:24: target.ensureIntersectionObserverData().addObservation(*this); Yeah it's nice if the constructor has no side effects and instead the caller does the hook up. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:47: rootLayoutBox->flipForWritingMode(rect); ugh, writing mode incantations all over. Do we have a test for this? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:75: static void mapRectToDocumentCoordinates(LayoutObject* layoutObject, LayoutRect& rect) reference for layoutObject https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:86: if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText()) hmm, how can target be a Text? observe() on a Text should throw, can an Element get a Text layouter? Maybe <br> ? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:27: IntersectionObserver* observer() { return m_observer; } can these ever be null? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:29: while (rootMargin.size() < 5 && tokenRange.peek().type() != EOFToken && !exceptionState.hadException()) { On 2015/12/22 at 01:13:30, haraken wrote: > Where does 5 come from? It's parsing a sequence of lengths, this could use a comment. It wants to accept: 5px = all sides 5px 5px = top bottom 5px 5px 5px = top left/right bottom 5px 5px 5px 5px = top right left bottom See: https://developer.mozilla.org/en-US/docs/Web/CSS/margin#Syntax https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:146: bool IntersectionObserver::isDescendantOfRoot(const Element* target) const On 2015/12/22 at 01:13:30, haraken wrote: > It would be helpful to have a comment to explain what the "root" means. It's the m_root member variable. The only special part this method checks is that it crosses frame boundaries. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:234: checkRootAndDetachIfNeeded(); having to call this in each method still feels wrong, Can we use an Oilpan Weak callback instead? Can you add a TODO to this manual clean up method? Other features should not be following this pattern. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:246: static int computeMargin(const Length& length, LayoutUnit referenceLength) can this return LayoutUnit instead? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:260: rect.setX(rect.x() - leftMargin); LayoutRect is of LayoutUnit, so you convert from LayoutUnit above to int and then back to LayoutUnit, can we stay in LayoutUnit space without doing that conversion? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:319: // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take On 2015/12/22 at 01:13:30, haraken wrote: > I'm concerned about this but will comment on it after other lifetime issues are resolved :) Yeah this doesn't seem like something we want with Oilpan, how can we fix this feature or Oilpan? I think what's missing here is weak processing callbacks? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:341: // TODO(szager): should we deliver pending notifications? Pending records need to keep the root alive, otherwise there's no way to compute the data about the root like the bounds. So m_root should never be collected while there's records awaiting delivery. We might need to change the ownership model to make that work. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:65: WeakPtrWillBeWeakMember<Element> m_root; On 2015/12/22 at 01:13:30, haraken wrote: > If you use a WeakMember, isn't there any risk that Oilpan's GC behavior becomes observable to JS? The timing when the WeakMember is cleared depends on when Oilpan's GC runs. It is not a good idea that the behavior is exposed to JS. > > When should the m_root become nullptr? That must be explicitly defined without relying on a GC timing. JS can't get the root from the observer, that's not observable. The observer must not keep the root alive though, it also must not keep the targets alive. Only observations keep them alive. The only way to observe any of this is to have strong refs from JS, so I think this WeakPtr is fine. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:66: HeapHashSet<WeakMember<IntersectionObservation>> m_observations; On 2015/12/22 at 01:13:30, haraken wrote: > I'm wondering why this needs to be weak. > > You're already adding HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> to ElementIntersectionObserverData. Doesn't it mean that you're already adding a strong reference from the IntersectionObserver to the IntersectionObservation? Hmm yeah, shouldn't the observer keep the observations alive? Cycles in the Oilpan heap should be fine. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:11: typedef HeapVector<Member<IntersectionObserver>> IntersectionObserverVector; On 2015/12/22 at 01:13:30, haraken wrote: > Define this in the header file and use it in the header file as well. I'd rather we just typed the type out and removed this https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:38: HeapHashSet<WeakMember<IntersectionObserver>> m_trackedIntersectionObservers; On 2015/12/22 at 01:13:30, haraken wrote: > What's the relationship between m_trackedIntersectionObservers, m_activeIntersectionObservers and m_suspendedIntersectionObservers? If x is in m_trackedIntersectionObservers, is it guaranteed that x is in either of m_activeIntersectionObservers or m_suspendedIntersectionObservers? > > If so, I'm wondering why m_trackedIntersectionObservers needs to be weak members. Can you and Stefan VC and talk about this? I know there's a holiday that's making this move slowly. In general I think the lifetimes need to be figured out and you two need to come to agreement about the right way to use Oilpan here. Going around in codereview is difficult. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:36: RefPtrWillBeMember<Element> m_target; The cycle is fine, it's only until these records are delivered. If JS keeps a handle to the record it should also keep the element alive. This RefPtr is correct. We just need to make sure to either clear or deliver all records. This is the same as MutationObserver.
https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h:24: void handleEvent(HeapVector<Member<IntersectionObserverEntry>>&, IntersectionObserver&) override; On 2015/12/22 01:13:29, haraken wrote: > > const HeapVector<>& Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp:45: IntersectionObserver* observer = IntersectionObserver::create(intersectionObserverInit, *callback, exceptionState); On 2015/12/22 01:13:29, haraken wrote: > > I think you can just pass callback instead of *callback. Previous comment from esprehn@ suggested this parameter should be a reference rather than a pointer, since it can never be null. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/bindings/core/v8/custom/V8IntersectionObserverCustom.cpp:47: V8ThrowException::throwGeneralError(info.GetIsolate(), exceptionState.message()); On 2015/12/22 01:13:29, haraken wrote: > > Why do you need this GeneralError? > > When writing custom bindings, please copy & paste auto-generated code and modify > only the part you really need to modify. IntersectionObserver::create will return NULL and set an exception in exceptionState if it encounters an error. Is there a better way to propagate the error to v8? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:615: } On 2015/12/22 01:13:29, haraken wrote: > > I think we should call these in Document::detach, not Document::dispose. > Remember that Document::dispose is not called in oilpan. In oilpan, it should not be necessary to run this code. Even without oilpan, this code is mostly defensive: I want to make sure that m_intersectionObserverController does not prevent any IntersectionObservers from being garbage collected on the next gc run. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5073: IntersectionObserverController& Document::intersectionObserverController() On 2015/12/22 01:13:29, haraken wrote: > > intersectionObserverController() => ensureIntersectionObserverController() Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1463: rareData->ensureIntersectionObserverData().activateValidIntersectionObservers(*this); On 2015/12/22 01:13:29, haraken wrote: > > It looks a bit strange that you call ensureXXX after checking hasXXX. Shall we > change the signatures as follows? > > IntersectionObserverData* intersectionObserverData(); // Returns nullptr if > there is no IntersectionObserverData. > IntersectionObserverData& ensureIntersectionObserverData(); > > Then you can write: > > if (intersectionObserverData()) > intersectionObserverData()->...; Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1544: data->ensureIntersectionObserverData().deactivateAllIntersectionObservers(*this); On 2015/12/22 01:13:29, haraken wrote: > > Ditto. Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: m_intersectionObservations.set(observation.observer(), &observation); On 2015/12/22 01:13:29, haraken wrote: > > Add ASSERT(!m_intersectionObservations.contains(...)); Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:42: if (it != m_intersectionObservations.end()) On 2015/12/22 01:13:29, haraken wrote: > > Can we change this to: > > ASSERT(it != m_intersectionObservations.end()); > > ? This comes from an API call: observer.unobserve(target); It may fail with invalid target, but it should not ASSERT. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:57: void ElementIntersectionObserverData::deactivateAllIntersectionObservers(Element& element) On 2015/12/22 01:13:29, haraken wrote: > > Just to confirm: If the Element and the Document die together, > Element::removedFrom is not called. This means that > deactivateAllIntersectionObservers is not called. This is not an issue, right? That should be fine, regular garbage collection should cover that. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:37: // IntersectionObservers for which the Element owning this data is root. On 2015/12/22 01:13:29, haraken wrote: > > Help me understand: Is it possible that a non-Document Element becomes a root of > an IntersectionObserver? I was assuming that a target can be any Element but a > root is a Document. The root can be any container element; see the spec: http://rawgit.com/slightlyoff/IntersectionObserver/master/index.html#intersec... > Another question is: why do we need both > ElementIntersectionObserverData::m_intersectionObservers and > IntersectionObseverController::m_trackedObservers? I don't understand why we > need to keep track of a hash set of weak members in two places. IntersectionObserverController is associated with a Document, and it is responsible for running the intersection algorithm and generating notifications. The reason it is associated with a document is because the algorithm runs during FrameView::updateAllLifecyclePhases, at a point where the document is known to be layout and compositing clean. ElementIntersectionObserverData is associated with an Element, and it tracks IntersectionObservers for which the given element is either root or target. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; On 2015/12/22 01:13:29, haraken wrote: > > I don't fully understand why we need this mapping. Wouldn't it be more > straightforward if we have the following two HashSets? > > - ElementIntersectionObserverData retains HashSet<Member<IntersectionObserver>>. > - IntersectionObserver retains HashSet<Member<IntersectionObservation>>. Please take a look at the design doc linked in CL description: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... An IntersectionObservation must be kept alive by its target Element, hence the hard reference. If the target Element goes away, then the IntersectionObservation should go away (i.e., the IntersectionObserver should *not* keep it alive). https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:43: OwnPtrWillBeMember<WeakPtrFactory<Element>> m_weakPointerFactory; On 2015/12/22 01:13:30, haraken wrote: > > This can simply be: > > WeakPtrFactory<Element> > > OwnPtrWillBeMember is not needed. WeakPtrFactory must be constructed with a pointer to the object it generates WeakPtr's for, which isn't available in the ElementIntersectionObserverData constructor. Rather than plumbing the object pointer all the way down (which overly-complicates the methods), I just construct it lazily at a time when the object pointer is available. I went ahead and changed OwnPtrWillBeMember to OwnPtr, since this code will be compiled when oilpan is enabled. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:24: target.ensureIntersectionObserverData().addObservation(*this); On 2015/12/22 01:13:30, haraken wrote: > > The logic around here is messy... > > - IntersectionObserver::m_observations.add is called by IntersectionObserver. > - IntersectionObserver::m_observations.remove is called by > IntersectionObservation::disconnect. > - ElementIntersectionObserverData::m_intersectionObservations.add is called by > IntersectionObservation's constructor. > - ElementIntersectionObserverData::m_intersectionObservations.remove is called > by IntersectionObservation::disconnect. > > Can we refactor the code as follows? > > - IntersectionObservation's constructor calls > ElementIntersectionObserverData::addObservation. It calls > IntersectionObserver::m_observations.add and > ElementIntersectionObserverData::m_intersectionObservations.add. > - IntersectionObservation::disconnect calls > ElementIntersectionObserverData::removeObservation. It calls > IntersectionObserver::m_observations.remove and > ElementIntersectionObserverData::m_intersectionObservations.remove. I think this would be much more confusing, because it obscures the fact that two different instances of ElementIntersetionObserverData are touched: one for the root, and another for the target. Calling from the target ElementIntersectionObserverData instance into the root ElementIntersectionObserverData is subtle and confusing. I'll think about other ways to refactor this for clarity. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:153: m_target->ensureIntersectionObserverData().removeObservation(*this->observer()); On 2015/12/22 01:13:30, haraken wrote: > > I think you should clear m_target here. Can you explain why? In oilpan world, IntersectionObservation and m_target will create a reference cycle (since I have changed m_target to be a Member), so they should get gc'ed together. In pre-oilpan world, m_target is a weak reference, so IntersectionObserver will not keep it alive. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:18: IntersectionObservation(IntersectionObserver&, Element&, bool shouldReportRootBounds); On 2015/12/22 01:13:30, haraken wrote: > > Can we add a destructor and add ASSERT(!m_target)? We need to make sure that > disconnect() has been called before the IntersectionObserver gets destructed. > It should be OK to destruct an IntersectionObservation without calling disconnect(). In fact, in an oilpan world, that *must* be the case, because there is no opportunity to call disconnect() from the target element destructor, and the target element holds the only hard reference to the IntersectionObservation. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:48: WeakPtrWillBeWeakMember<Element> m_target; On 2015/12/22 01:13:30, haraken wrote: > > In oilpan, why does this need to be a WeakMember? Given that the > IntersectionObservation should not outlive the Element, it should be a Member. The IntersectionObservation should not > > In non-oilpan, I guess this doesn't need to be a WeakPtr. We can make it a > RawPtr and clear it in disconnect(). I'm assuming that disconnect() is always > called before the IntersectionObservation is destructed, right? You're right that in oilpan, it doesn't need to be weak. In non-oilpan, the observation should not keep the element alive, and as I said in the previous comment, there is no guarantee that disconnect() will be called. I changed it to WeakPtrWillBeMember https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:29: while (rootMargin.size() < 5 && tokenRange.peek().type() != EOFToken && !exceptionState.hadException()) { On 2015/12/22 01:13:30, haraken wrote: > > Where does 5 come from? It accepts syntax similar to padding, e.g.: "1px" "1px 2px" "1px 2px 3px" "1px 2px 3px 4px" Currently, it ignores any extra stuff at the end. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:146: bool IntersectionObserver::isDescendantOfRoot(const Element* target) const On 2015/12/22 01:13:30, haraken wrote: > > It would be helpful to have a comment to explain what the "root" means. Added a comment. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:216: void IntersectionObserver::disconnect(IntersectionObservation& observation) On 2015/12/22 01:13:30, haraken wrote: > > Nit: We can just pass IntersectionObservation* instead of > IntersectionObservation&. This is contrary to esprehn's comments. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:218: m_observations.remove(&observation); On 2015/12/22 01:13:30, haraken wrote: > > Add ASSERT(m_observations.contains(observation)). Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:319: // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take On 2015/12/22 01:13:30, haraken wrote: > > I'm concerned about this but will comment on it after other lifetime issues are > resolved :) Just to be clear: I don't think your current round of comments on this CL have raised any new issues about lifetime management, so please do weigh in on this method. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:65: WeakPtrWillBeWeakMember<Element> m_root; On 2015/12/22 01:13:30, haraken wrote: > > If you use a WeakMember, isn't there any risk that Oilpan's GC behavior becomes > observable to JS? The timing when the WeakMember is cleared depends on when > Oilpan's GC runs. It is not a good idea that the behavior is exposed to JS. > > When should the m_root become nullptr? That must be explicitly defined without > relying on a GC timing. m_root should become nullptr when the Element it points to is deleted. Is there some other way to do this, aside from waiting for gc to run? https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.h:66: HeapHashSet<WeakMember<IntersectionObservation>> m_observations; On 2015/12/22 01:13:30, haraken wrote: > > I'm wondering why this needs to be weak. > > You're already adding HeapHashMap<Member<IntersectionObserver>, > Member<IntersectionObservation>> to ElementIntersectionObserverData. Doesn't it > mean that you're already adding a strong reference from the IntersectionObserver > to the IntersectionObservation? No, I think you are confused about the members of ElementIntersectionObserverData. An Element may be the root of one observer, and the target of another. ElementIntersectionObserverData keeps track of both. An IntersectionObserver may outlive its IntersectionObservations (if the target element goes away). The observer should not keep the observation alive; only the target element keeps the observation alive. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.idl (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.idl:5: // http://w3c.github.io/performance-timeline/#idl-def-PerformanceObserverCallback On 2015/12/22 01:13:30, haraken wrote: > > The spec link is wrong. Replaced with a TODO. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:11: typedef HeapVector<Member<IntersectionObserver>> IntersectionObserverVector; On 2015/12/22 01:13:30, haraken wrote: > > Define this in the header file and use it in the header file as well. This is contrary to comments received from esprehn. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:78: m_trackedIntersectionObservers.remove(observer); On 2015/12/22 01:13:30, haraken wrote: > > You can use removeAll. Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:37: // IntersectionObservers for which this is the tracking document. On 2015/12/22 01:13:30, haraken wrote: > > Can you add a link to the dcheng's document > (https://docs.google.com/document/d/1gmIi87dPJ0zu6XU7Jk2t8JS6b-Pig7gbkwZThxZjz...), > or add a good comment about the lifetime relationship of the > IntersectionObserver-related objects somewhere? Added a link to the design doc, which should be the ultimate resource for all of this stuff. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:38: HeapHashSet<WeakMember<IntersectionObserver>> m_trackedIntersectionObservers; On 2015/12/22 01:13:30, haraken wrote: > > What's the relationship between m_trackedIntersectionObservers, > m_activeIntersectionObservers and m_suspendedIntersectionObservers? If x is in > m_trackedIntersectionObservers, is it guaranteed that x is in either of > m_activeIntersectionObservers or m_suspendedIntersectionObservers? > > If so, I'm wondering why m_trackedIntersectionObservers needs to be weak > members. > m_trackedIntersectionObservers are observers for which the owning Document is responsible for running the intersection algorithm and generating notifications. That's a weak member because the Document should not keep the observer alive (e.g., if the observer's root goes away). m_activeIntersectionObservers are observers that have pending notifications to be sent out. m_suspendedIntersectionObservers are observes that have pending notifications to be sent out, but sending notifications has been delayed because the js execution context is suspended. The notifications will be sent when the execution context resumes. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:36: RefPtrWillBeMember<Element> m_target; On 2015/12/22 01:13:30, haraken wrote: > > In non-oilpan, why doesn't this RefPtr create a cycle? > > IntersectionObserver ==(Member)=> IntersectionObserverEntry ==(RefPtr)=> > Element ==(RefPtr)=> Document ==(Persistent)=> IntersectionObserverController > ==(Member)=> IntersectionObserver The IntersectionObserver stops pointing to the IntersectionObserverEntry when the callback runs. See IntersectionObserver::deliver(). https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl:5: // Spec: http://dom.spec.whatwg.org/#idl-def-IntersectionObserverInit On 2015/12/22 01:13:30, haraken wrote: > > This link doesn't exist. Replaced with TODO.
Thanks for the update :) I'll be running into a vacation, so let's chat about the design at the beginning of the next year. I'll take another detailed look by then.
I should be up-to-date with the latest comments from esprehn and haraken. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:36: m_intersectionObservations.set(observation.observer(), &observation); On 2015/12/22 07:50:32, esprehn wrote: > On 2015/12/22 at 01:13:29, haraken wrote: > > Add ASSERT(!m_intersectionObservations.contains(...)); > > Since this is a HashMap this should be .add() not .set() I think? No reason to > check .contains() Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:88: return element; On 2015/12/22 07:50:32, esprehn wrote: > does this actually work? The return type doesn't need to be something like > WeakPtrWillBeRawPtr? Good catch! https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:24: target.ensureIntersectionObserverData().addObservation(*this); On 2015/12/22 07:50:32, esprehn wrote: > Yeah it's nice if the constructor has no side effects and instead the caller > does the hook up. Moved the addObservation() call into the caller. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:47: rootLayoutBox->flipForWritingMode(rect); On 2015/12/22 07:50:32, esprehn wrote: > ugh, writing mode incantations all over. Do we have a test for this? No, this was cargo culted from LayoutBox::applyCachedClipAndScrollOffsetForPaintInvalidation. Added a TODO to write a test. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:75: static void mapRectToDocumentCoordinates(LayoutObject* layoutObject, LayoutRect& rect) On 2015/12/22 07:50:32, esprehn wrote: > reference for layoutObject Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:86: if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText()) On 2015/12/22 07:50:32, esprehn wrote: > hmm, how can target be a Text? observe() on a Text should throw, can an Element > get a Text layouter? Maybe <br> ? Yeah, I think <br> would do it. And I think there was interest in extending the spec to cover text nodes at some point. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:27: IntersectionObserver* observer() { return m_observer; } On 2015/12/22 07:50:32, esprehn wrote: > can these ever be null? No, switched to reference. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:29: while (rootMargin.size() < 5 && tokenRange.peek().type() != EOFToken && !exceptionState.hadException()) { On 2015/12/22 07:50:32, esprehn wrote: > On 2015/12/22 at 01:13:30, haraken wrote: > > Where does 5 come from? > > It's parsing a sequence of lengths, this could use a comment. > > It wants to accept: > > 5px = all sides > 5px 5px = top bottom > 5px 5px 5px = top left/right bottom > 5px 5px 5px 5px = top right left bottom > > See: > https://developer.mozilla.org/en-US/docs/Web/CSS/margin#Syntax Added comment. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:234: checkRootAndDetachIfNeeded(); On 2015/12/22 07:50:33, esprehn wrote: > having to call this in each method still feels wrong, Can we use an Oilpan Weak > callback instead? Can you add a TODO to this manual clean up method? > > Other features should not be following this pattern. I agree wholeheartedly, that is the next thing I plan to work on. There is already a TODO on the method definition. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:246: static int computeMargin(const Length& length, LayoutUnit referenceLength) On 2015/12/22 07:50:33, esprehn wrote: > can this return LayoutUnit instead? Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:260: rect.setX(rect.x() - leftMargin); On 2015/12/22 07:50:32, esprehn wrote: > LayoutRect is of LayoutUnit, so you convert from LayoutUnit above to int and > then back to LayoutUnit, can we stay in LayoutUnit space without doing that > conversion? Done. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:319: // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take On 2015/12/22 07:50:32, esprehn wrote: > On 2015/12/22 at 01:13:30, haraken wrote: > > I'm concerned about this but will comment on it after other lifetime issues > are resolved :) > > Yeah this doesn't seem like something we want with Oilpan, how can we fix this > feature or Oilpan? I think what's missing here is weak processing callbacks? registerWeakMembers looks like it might work here, I'm investigating. https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:341: // TODO(szager): should we deliver pending notifications? On 2015/12/22 07:50:33, esprehn wrote: > Pending records need to keep the root alive, otherwise there's no way to compute > the data about the root like the bounds. So m_root should never be collected > while there's records awaiting delivery. We might need to change the ownership > model to make that work. Once I figure out a non-leaky fix for the root lifetime issue, it shouldn't be necessary to do this.
https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:218: m_observations.remove(&observation); On 2015/12/22 01:13:30, haraken wrote: > > Add ASSERT(m_observations.contains(observation)). Actually, no, that's a bad ASSERT. Because m_observations is a hash set of weak pointers, it's possible (and it's OK) for the observation to be missing.
Thanks for the update! Let me ask a couple of questions about the design in this document (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...). - Why does the reference from Observer to Observation need to be weak? I guess it could be a strong reference. - This is a question against the spec, but I don't fully understand why we need a concept of the "root" element. The root element adds a lot of complexity to the implementation. What happens if we simply remove the concept of the "root" element and associate the Observer with the "tracking document"? I'm feeling that having the two notions "root element" and "tracking document" is a bit too complicated in particular when the observer moves from one document to another. - What do you mean by "Callback Context"? If it means a ScriptState (which is in 1:1 relationship with v8::Context of the frame), the design makes sense. However, the description "Callback Context is the execution context (document) in which the observer was created, and it is where notification callbacks run" doesn't make sense. Maybe you can just rename "Callback Context" to "ScriptState" or "v8::Context"? Or you can just remove it from the design since it's not the core part of the object lifetime relationship.
On 2015/12/28 05:54:55, haraken wrote: > > - This is a question against the spec, but I don't fully understand why we need > a concept of the "root" element. The root element adds a lot of complexity to > the implementation. What happens if we simply remove the concept of the "root" > element and associate the Observer with the "tracking document"? I'm feeling > that having the two notions "root element" and "tracking document" is a bit too > complicated in particular when the observer moves from one document to another. > The "root" element is meant to solve the "infinite scrolling" use-case. If the scrolling container is used as the "root", then the application developer can make a widget whose behavior is consistent and correct no matter where the widget is placed on screen. I.e. it can lazy-load and pre-fetch and pre-paint correctly based on how far its elements are from the visible part of the container. If the "root" concept was dropped, then the widget would get different behavior based on how it was scrolled or clipped by the parent page, and loses the ability to ask for callbacks when content is getting close to being in view (via the rootMargin modifier.)
On 2015/12/28 05:54:55, haraken wrote: > Thanks for the update! > > Let me ask a couple of questions about the design in this document > (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...). > > - Why does the reference from Observer to Observation need to be weak? I guess > it could be a strong reference. The Observer should not keep the Observation alive, i.e., if the target element gets garbage collected, the Observation should go away. > - This is a question against the spec, but I don't fully understand why we need > a concept of the "root" element. The root element adds a lot of complexity to > the implementation. What happens if we simply remove the concept of the "root" > element and associate the Observer with the "tracking document"? I'm feeling > that having the two notions "root element" and "tracking document" is a bit too > complicated in particular when the observer moves from one document to another. As Michael mentioned, there are common use cases that require the root element, the most common of which is content inside a scrolling div. > - What do you mean by "Callback Context"? If it means a ScriptState (which is in > 1:1 relationship with v8::Context of the frame), the design makes sense. > However, the description "Callback Context is the execution context (document) > in which the observer was created, and it is where notification callbacks run" > doesn't make sense. Maybe you can just rename "Callback Context" to > "ScriptState" or "v8::Context"? Or you can just remove it from the design since > it's not the core part of the object lifetime relationship. "Execution context" refers to an instance of this class: https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... ... which will be the Document whose js context created the observer. The callback associated with the observer will hold a strong reference to the ExecutionContext and keep it alive.
Thanks for the clarification! On 2015/12/28 19:06:20, szager1 wrote: > On 2015/12/28 05:54:55, haraken wrote: > > Thanks for the update! > > > > Let me ask a couple of questions about the design in this document > > > (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...). > > > > - Why does the reference from Observer to Observation need to be weak? I guess > > it could be a strong reference. > > The Observer should not keep the Observation alive, i.e., if the target element > gets garbage collected, the Observation should go away. Makes sense. > > - This is a question against the spec, but I don't fully understand why we > need > > a concept of the "root" element. The root element adds a lot of complexity to > > the implementation. What happens if we simply remove the concept of the "root" > > element and associate the Observer with the "tracking document"? I'm feeling > > that having the two notions "root element" and "tracking document" is a bit > too > > complicated in particular when the observer moves from one document to > another. > > As Michael mentioned, there are common use cases that require the root element, > the most common of which is content inside a scrolling div. Makes sense. > > - What do you mean by "Callback Context"? If it means a ScriptState (which is > in > > 1:1 relationship with v8::Context of the frame), the design makes sense. > > However, the description "Callback Context is the execution context (document) > > in which the observer was created, and it is where notification callbacks run" > > doesn't make sense. Maybe you can just rename "Callback Context" to > > "ScriptState" or "v8::Context"? Or you can just remove it from the design > since > > it's not the core part of the object lifetime relationship. > > "Execution context" refers to an instance of this class: > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > ... which will be the Document whose js context created the observer. The > callback associated with the observer will hold a strong reference to the > ExecutionContext and keep it alive. Hmm, I'm a bit confused. Maybe I'm not understanding the relationship between the Callback Context and the Tracking Document. - Is it possible that the Tracking Document is different from the Callback Context? If yes, why do we need the behavior? - Imagine that initially both the Calling Context and the Tracking Document are a document A. The root element and the target element are contained in the document A. I want to understand what happens (1) when the target element is moved from the document A to another document B and (2) when the root element is moved from the document A to another document B. (1) When the target element is moved from the document A to the document B, the Observation of the target element is removed from the Observer. That's it. (2) When the root element is moved from the document A to the document B (but the target element stays on the document A), what happens? I think you're trying to move the Observer to the document B. At this point, the Tracking Document becomes the document B while the Calling Context remains as the document A. This sounds way too complicated (in terms of both the Blink implementation and the semantics for web developers), but why do we need the behavior? What happens if we simply remove the Observer when the root element is moved to the document B? - Why does the reference from the root element to the Observer need to be weak? I'm wondering why root => Observer is weak whereas target => Observation is strong.
On 2015/12/29 02:39:13, haraken wrote: > > > - What do you mean by "Callback Context"? If it means a ScriptState (which > is > > in > > > 1:1 relationship with v8::Context of the frame), the design makes sense. > > > However, the description "Callback Context is the execution context > (document) > > > in which the observer was created, and it is where notification callbacks > run" > > > doesn't make sense. Maybe you can just rename "Callback Context" to > > > "ScriptState" or "v8::Context"? Or you can just remove it from the design > > since > > > it's not the core part of the object lifetime relationship. > > > > "Execution context" refers to an instance of this class: > > > > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > > > ... which will be the Document whose js context created the observer. The > > callback associated with the observer will hold a strong reference to the > > ExecutionContext and keep it alive. > > Hmm, I'm a bit confused. Maybe I'm not understanding the relationship between > the Callback Context and the Tracking Document. The tracking document is the document that is responsible for running the algorithm to compute intersections and generate notifications. The tracking document will *always* be the document containing the root; if the root moves to a different document, the new document become the tracking document for the observer. The reason for specifying the "tracking document" relationship is for efficiency: the algorithm is run when the document is generating a frame, and it iterates through the document's list of tracked observers to compute intersections. If the document did not keep such a list, then the algorithm would have to walk the document's DOM tree to find observer roots. The document's list of tracked observers uses weak pointers because the document should not keep the observer alive, i.e., if the root is garbage collected, the observer should also be garbage collected. > - Imagine that initially both the Calling Context and the Tracking Document are > a document A. The root element and the target element are contained in the > document A. I want to understand what happens (1) when the target element is > moved from the document A to another document B and (2) when the root element is > moved from the document A to another document B. When the target element is moved to another document, the "tracking document" for the observer does not change and nothing gets garbage collected, but the target's observation will be marked inactive if the target is no longer a descendant (through the combined frame/DOM hierarchy) of the root. > (1) When the target element is moved from the document A to the document B, the > Observation of the target element is removed from the Observer. That's it. Not quite: the observation is marked inactive, but not removed from the observer. > (2) When the root element is moved from the document A to the document B (but > the target element stays on the document A), what happens? I think you're trying > to move the Observer to the document B. At this point, the Tracking Document > becomes the document B while the Calling Context remains as the document A. This > sounds way too complicated (in terms of both the Blink implementation and the > semantics for web developers), but why do we need the behavior? What happens if > we simply remove the Observer when the root element is moved to the document B? It's worth emphasizing that the "tracking document" relationship does not participate in the lifetime of either observers or observations. It is merely an implementation efficiency that is tied to the timing of when the algorithm runs (when the document is compositing clean). Because the tracking document only keeps weak pointers to observers, you can safely ignore that relationship when considering lifetime issues. I would also point out that what you propose -- deleting an observer when its root is inserted into a different document -- does not in any way solve the one actual lifetime issue that I have pointed out several times: there is no mechanism for disposing of an observer when its root gets garbage collected. > - Why does the reference from the root element to the Observer need to be weak? > I'm wondering why root => Observer is weak whereas target => Observation is > strong. The observer should not keep the root alive. If the target is intact, it holds a strong reference to the observation, which holds a strong reference to the observer. If the root is detached and has no incoming references other than the observer, then the root should be garbage collected.
Anyone available and willing to lgtm? We are very far behind schedule in getting this feature into the hands of testers.
Please see my earlier comment: Please move the rootMargin code to a followup patch. This patch is already way too huge. Anything that can be moved to followup patches really should. Is there anything other than the rootMargin code that could be extracted out into a followup patch as well? Big patches are impossible to do good reviews of and are also hard to make sense of for people doing code archaeology.
On 2015/12/29 21:12:45, ojan wrote: > Please see my earlier comment: Please move the rootMargin code to a followup > patch. This patch is already way too huge. Anything that can be moved to > followup patches really should. Is there anything other than the rootMargin code > that could be extracted out into a followup patch as well? > > Big patches are impossible to do good reviews of and are also hard to make sense > of for people doing code archaeology. That's helpful. Also we've scheduled a sync-up to chat about the lifetime issue at the first day of Jan. Let's reach consensus before landing this CL.
(Since I'm now OOO, the best thing I can do is to post comments in the code review even though it might not be an efficient way to address the lifetime issues. Hope it helps to unblock your work a bit.) On 2015/12/29 10:37:12, szager1 wrote: > On 2015/12/29 02:39:13, haraken wrote: > > > > > - What do you mean by "Callback Context"? If it means a ScriptState (which > > is > > > in > > > > 1:1 relationship with v8::Context of the frame), the design makes sense. > > > > However, the description "Callback Context is the execution context > > (document) > > > > in which the observer was created, and it is where notification callbacks > > run" > > > > doesn't make sense. Maybe you can just rename "Callback Context" to > > > > "ScriptState" or "v8::Context"? Or you can just remove it from the design > > > since > > > > it's not the core part of the object lifetime relationship. > > > > > > "Execution context" refers to an instance of this class: > > > > > > > > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > > > > > ... which will be the Document whose js context created the observer. The > > > callback associated with the observer will hold a strong reference to the > > > ExecutionContext and keep it alive. > > > > Hmm, I'm a bit confused. Maybe I'm not understanding the relationship between > > the Callback Context and the Tracking Document. > > The tracking document is the document that is responsible for running the > algorithm to compute intersections and generate notifications. The tracking > document will *always* be the document containing the root; if the root moves to > a different document, the new document become the tracking document for the > observer. > > The reason for specifying the "tracking document" relationship is for > efficiency: the algorithm is run when the document is generating a frame, and it > iterates through the document's list of tracked observers to compute > intersections. If the document did not keep such a list, then the algorithm > would have to walk the document's DOM tree to find observer roots. Thanks, now I understand what the tracking document is. That makes sense. Then getting back to the original question: Why do we need to keep alive the Calling Context? You said that the Calling Context is the ExecutionContext that created the Observer. Why do we need to keep the original ExecutionContext to run the Observer/Callback? Specifically, how is the ExecutionContext kept alive in the CL? (As I mentioned above, if you mean the ScriptState (or its associated v8::Context) by the Calling Context, my question goes away.) > The document's list of tracked observers uses weak pointers because the document > should not keep the observer alive, i.e., if the root is garbage collected, the > observer should also be garbage collected. > > > > - Imagine that initially both the Calling Context and the Tracking Document > are > > a document A. The root element and the target element are contained in the > > document A. I want to understand what happens (1) when the target element is > > moved from the document A to another document B and (2) when the root element > is > > moved from the document A to another document B. > > When the target element is moved to another document, the "tracking document" > for the observer does not change and nothing gets garbage collected, but the > target's observation will be marked inactive if the target is no longer a > descendant (through the combined frame/DOM hierarchy) of the root. > > > > (1) When the target element is moved from the document A to the document B, > the > > Observation of the target element is removed from the Observer. That's it. > > Not quite: the observation is marked inactive, but not removed from the > observer. It is not possible to re-activate the observation, right? I'm wondering why you can't simply remove the observation from the observer. > > > (2) When the root element is moved from the document A to the document B (but > > the target element stays on the document A), what happens? I think you're > trying > > to move the Observer to the document B. At this point, the Tracking Document > > becomes the document B while the Calling Context remains as the document A. > This > > sounds way too complicated (in terms of both the Blink implementation and the > > semantics for web developers), but why do we need the behavior? What happens > if > > we simply remove the Observer when the root element is moved to the document > B? > > It's worth emphasizing that the "tracking document" relationship does not > participate in the lifetime of either observers or observations. It is merely > an implementation efficiency that is tied to the timing of when the algorithm > runs (when the document is compositing clean). Because the tracking document > only keeps weak pointers to observers, you can safely ignore that relationship > when considering lifetime issues. > > I would also point out that what you propose -- deleting an observer when its > root is inserted into a different document -- does not in any way solve the one > actual lifetime issue that I have pointed out several times: there is no > mechanism for disposing of an observer when its root gets garbage collected. I think there is a mechanism. Remember that the root element and the document are in the same DOM tree. This means that either of the following scenarios must happen when the root element dies. 1) The root element and the document die at the same time. 2) The root element is removed from the document (and may die). Regarding 1), you don't need to remove the observer because the document dies at the same time anyway. Regarding 2), you can remove the observer in Element::removedFrom(). In other words, you can assume that when an element dies, Element::removedFrom() is called or the element dies with the associated document. We already use the assumption in many places in the code base. This also implies that you can make the document's list of tracked observers strong pointers. > > > - Why does the reference from the root element to the Observer need to be > weak? > > I'm wondering why root => Observer is weak whereas target => Observation is > > strong. > > The observer should not keep the root alive. If the target is intact, it holds > a strong reference to the observation, which holds a strong reference to the > observer. If the root is detached and has no incoming references other than the > observer, then the root should be garbage collected. I understand why Observer => root needs to be weak. My question is why root => Observer needs to be weak (whereas target => Observation is strong).
dbc trifles + looks like a rebase is in order. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:47: } // namespace blink { nit: drop " {" (here & elsewhere in various spots.) https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:9: #include "wtf/RefPtr.h" not needed https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:10: #include "wtf/Vector.h" not needed. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:70: void IntersectionObserverController::removeTrackedObserversForRoot(Element& root) const Element& possible? https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:72: HeapVector<Member<IntersectionObserver>> toRemove; IntersectionObserverVector? https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:13: #include "wtf/RefCounted.h" Not needed. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:17: class Document; not needed. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:18: template<typename T> class Timer; shouldn't be needed here (now?) https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:49: #endif // IntersectionObservationRegistry_h doesn't quite match up.
On 2015/12/30 00:57:38, haraken wrote: > Thanks, now I understand what the tracking document is. That makes sense. > > Then getting back to the original question: > > Why do we need to keep alive the Calling Context? You said that the Calling > Context is the ExecutionContext that created the Observer. Why do we need to > keep the original ExecutionContext to run the Observer/Callback? Specifically, > how is the ExecutionContext kept alive in the CL? > > (As I mentioned above, if you mean the ScriptState (or its associated > v8::Context) by the Calling Context, my question goes away.) The callback class V8IntersectionObserverCallback inherits from ActiveDOMCallback, which holds a strong reference to the Document. I don't believe this is unusual in any way (V8MutationCallback does the same). > > > (1) When the target element is moved from the document A to the document B, > > the > > > Observation of the target element is removed from the Observer. That's it. > > > > Not quite: the observation is marked inactive, but not removed from the > > observer. > > It is not possible to re-activate the observation, right? I'm wondering why you > can't simply remove the observation from the observer. It *is* possible to reactivate the observation. If root or target is removed from the DOM and the reinserted, and the hierarchical relationship between root and target is still valid, then the observation is automatically reactivated. > > I would also point out that what you propose -- deleting an observer when its > > root is inserted into a different document -- does not in any way solve the > one > > actual lifetime issue that I have pointed out several times: there is no > > mechanism for disposing of an observer when its root gets garbage collected. > > I think there is a mechanism. Remember that the root element and the document > are in the same DOM tree. This means that either of the following scenarios must > happen when the root element dies. > > 1) The root element and the document die at the same time. > 2) The root element is removed from the document (and may die). > > Regarding 1), you don't need to remove the observer because the document dies at > the same time anyway. > > Regarding 2), you can remove the observer in Element::removedFrom(). Nope, Element::removedFrom will mark the observer inactive (more precisely, it will mark all of its observations inactive), but the observer doesn't get deleted. > In other words, you can assume that when an element dies, Element::removedFrom() > is called or the element dies with the associated document. We already use the > assumption in many places in the code base. > > This also implies that you can make the document's list of tracked observers > strong pointers. Again: observers and observations cannot be deleted in removedFrom, they can only be deleted when the root or target element (respectively) is deleted. The only time we are *sure* it's OK to delete an observation is when its target element is deleted, and that happens already because only the target element holds a strong reference to the observation. But the situation for the observer is complicated is more complicated. > > > - Why does the reference from the root element to the Observer need to be > > weak? > > > I'm wondering why root => Observer is weak whereas target => Observation is > > > strong. target->observation is strong because once the target goes away, there's no sense in keeping the observation around, it can never be useful again. root->observer is weak because if an observer has no javascript references and no observations, then it is unreachable and should be deleted. A strong reference from root to observer would keep the observer alive.
On 2015/12/30 18:43:05, szager1 wrote: > On 2015/12/30 00:57:38, haraken wrote: > > > Thanks, now I understand what the tracking document is. That makes sense. > > > > Then getting back to the original question: > > > > Why do we need to keep alive the Calling Context? You said that the Calling > > Context is the ExecutionContext that created the Observer. Why do we need to > > keep the original ExecutionContext to run the Observer/Callback? Specifically, > > how is the ExecutionContext kept alive in the CL? > > > > (As I mentioned above, if you mean the ScriptState (or its associated > > v8::Context) by the Calling Context, my question goes away.) > > The callback class V8IntersectionObserverCallback inherits from > ActiveDOMCallback, which holds a strong reference to the Document. I don't > believe this is unusual in any way (V8MutationCallback does the same). That is a weak reference to the Document, not a strong reference. https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... If you need to keep a strong reference from the observer to the original document, that doesn't make sense to me. > > > > (1) When the target element is moved from the document A to the document > B, > > > the > > > > Observation of the target element is removed from the Observer. That's it. > > > > > > Not quite: the observation is marked inactive, but not removed from the > > > observer. > > > > It is not possible to re-activate the observation, right? I'm wondering why > you > > can't simply remove the observation from the observer. > > It *is* possible to reactivate the observation. If root or target is removed > from the DOM and the reinserted, and the hierarchical relationship between root > and target is still valid, then the observation is automatically reactivated. Would you help me understand why you need to support the reactivation? What happens if you just recreate a new observer when the root is reinserted to the original document? You're already creating a new observer when the root is inserted to a different document. It seems strange if we need a different logic depending on whether the observer is reinserted to the same document or not. (The reason I'm sticking to these issues is that I need to understand a big picture of the object lifetime to consider how to get the oilpan part of IntersectionObserver::checkRootAndDetachIfNeeded() right.) > > > I would also point out that what you propose -- deleting an observer when > its > > > root is inserted into a different document -- does not in any way solve the > > one > > > actual lifetime issue that I have pointed out several times: there is no > > > mechanism for disposing of an observer when its root gets garbage collected. > > > > I think there is a mechanism. Remember that the root element and the document > > are in the same DOM tree. This means that either of the following scenarios > must > > happen when the root element dies. > > > > 1) The root element and the document die at the same time. > > 2) The root element is removed from the document (and may die). > > > > Regarding 1), you don't need to remove the observer because the document dies > at > > the same time anyway. > > > > Regarding 2), you can remove the observer in Element::removedFrom(). > > Nope, Element::removedFrom will mark the observer inactive (more precisely, it > will mark all of its observations inactive), but the observer doesn't get > deleted. > > > > In other words, you can assume that when an element dies, > Element::removedFrom() > > is called or the element dies with the associated document. We already use the > > assumption in many places in the code base. > > > > This also implies that you can make the document's list of tracked observers > > strong pointers. > > Again: observers and observations cannot be deleted in removedFrom, they can > only be deleted when the root or target element (respectively) is deleted. > > The only time we are *sure* it's OK to delete an observation is when its target > element is deleted, and that happens already because only the target element > holds a strong reference to the observation. > > But the situation for the observer is complicated is more complicated. > > > > > > > - Why does the reference from the root element to the Observer need to be > > > weak? > > > > I'm wondering why root => Observer is weak whereas target => Observation > is > > > > strong. > > target->observation is strong because once the target goes away, there's no > sense in keeping the observation around, it can never be useful again. > > root->observer is weak because if an observer has no javascript references and > no observations, then it is unreachable and should be deleted. A strong > reference from root to observer would keep the observer alive. Makes sense. Bi-directional weak references are very uncommon and fragile, but it seems we need them here.
On 2016/01/01 02:26:15, haraken wrote: > On 2015/12/30 18:43:05, szager1 wrote: > > > > The callback class V8IntersectionObserverCallback inherits from > > ActiveDOMCallback, which holds a strong reference to the Document. I don't > > believe this is unusual in any way (V8MutationCallback does the same). > > That is a weak reference to the Document, not a strong reference. Ah, so it is. So when the root element goes away, it will not keep the callback context alive, but it will potentially leak the IntersectionObserver. Which is better, but still a problem. > > > > > (1) When the target element is moved from the document A to the document > > B, > > > > the > > > > > Observation of the target element is removed from the Observer. That's > it. > > > > > > > > Not quite: the observation is marked inactive, but not removed from the > > > > observer. > > > > > > It is not possible to re-activate the observation, right? I'm wondering why > > you > > > can't simply remove the observation from the observer. > > > > It *is* possible to reactivate the observation. If root or target is removed > > from the DOM and the reinserted, and the hierarchical relationship between > root > > and target is still valid, then the observation is automatically reactivated. > > Would you help me understand why you need to support the reactivation? What > happens if you just recreate a new observer when the root is reinserted to the > original document? You're already creating a new observer when the root is > inserted to a different document. It seems strange if we need a different logic > depending on whether the observer is reinserted to the same document or not. We do not recreate the observer when the root is inserted into a new document; we simply reactivate the existing observer. The only place IntersectionObserver is instantiated is in the binding to the javascript constructor. We need to support reactivation to make life easier for developers. Take the example of a social feed with infinite scroll: it's entirely likely that the page might reorder some items in the feed, and it would be a a nasty surprise for the developer if intersection observers were destroyed the moment root or target was removed from the DOM tree.
I think I now understand the lifetime relationship. The main points of my comments are as follows: - Consider removing ElementIntersectionObserverData::m_intersectionObservations. Once we remove it, we can remove IntersectionObservation::disconnect(). Then we can remove the following methods: --- ElementIntersectionObserverData::dispose() --- IntersectionObserver::disconnect(IntersectionObservation&) --- checkRootAndDetachIfNeeded() (<---- Removing this is our main goal.) - Remove the following methods: --- IntersectionObserver::dispose() --- IntersectionObserverController::dispose() - Don't support a behavior for resume/suspend in this CL. We can support it in a follow-up CL. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5120: void Document::tasksWereSuspended() Don't you need to suspend the observers when the document gets suspended? The only customer of suspend/resume is Devtools and a couple of unpopular features. I'd propose not supporting suspend/resume in this CL (i.e. remove IntersectionObserverController::m_suspendedIntersectionObservers). We can support it in a follow-up CL. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:110: #include "core/layout/LayoutPart.h" These #includes wouldn't be necessary. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1463: rareData->ensureIntersectionObserverData().activateValidIntersectionObservers(*this); Nit: It looks a bit strange to call ensureXXX after checking XXX. if (ElementIntersectionObserverData* data = rareData->intersectionObserverData()) data->...; https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1544: data->ensureIntersectionObserverData().deactivateAllIntersectionObservers(*this); Ditto. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:72: observation->disconnect(); Once we remove IntersectionObservation::disconnect(), we can remove this. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:78: observer->dispose(); This wouldn't be necessary. See my comment on IntersectionObserver::dispose. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:81: m_weakPointerFactory.clear(); This wouldn't be necessary. In conclusion, we can remove ElementIntersectionObserverData::dispose() completely. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; I think this hash map is key for getting rid of checkRootAndDetachIfNeeded(). I don't fully understand why this hash map is needed. - (As I commented in the previous review,) this hash map is adding a strong reference from the Observer to the Observation (whose target element is this element). We won't want to have the strong reference because the Observer shouldn't keep alive the Observation. - The Observer already knows a set of Observations. It looks strange if we have to keep track of the Observations on the target element as well. If we can remove this hash map, I think it's possible to remove IntersectionObservation::disconnect(). Then we no longer need checkRootAndDetachIfNeeded(). The problem will be solved. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:151: observer().disconnect(*this); This wouldn't be necessary because the Observation is removed from the Observer by Oilpan's weak processing. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:46: WeakPtrWillBeMember<Element> m_target; Doesn't this need to be a WeakMember? According to the design document (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...), observation => target needs to be weak. (Sorry, in the previous review I asked you to use a Member, not a WeakMember. I now understand the lifetime relationship and I think a WeakMember is a right solution.) https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:226: void IntersectionObserver::disconnect(IntersectionObservation& observation) As commented in IntersectionObserver::disconnect, I think you can remove this method. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:236: toDisconnect.append(observation); Use copyToVector. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:284: bool IntersectionObserver::shouldBeSuspended() const To reduce the complexity of this CL, I'd propose not supporting resume/suspend in this CL. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:319: void IntersectionObserver::dispose() I think you can remove this dispose method, for the following reasons. - You don't need to clear m_root because m_root is a weak reference. It is cleared out automatically (in both oilpan and non-oilpan). - The dispose() method is enabled only in non-oilpan. checkRootAndDetachIfNeeded() is no-op in non-oilpan. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:24: m_timer.startOneShot(0, BLINK_FROM_HERE); It's better to check m_timer.isActive() before starting a new shot. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:80: void IntersectionObserverController::dispose() This dispose() shouldn't be necessary even in non-Oilpan. Let's remove it. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:39: HeapHashSet<Member<IntersectionObserver>> m_activeIntersectionObservers; I'd rename this to m_pendingIntersectionObservers. "active" is confusing with the active/inactive state of the IntersectionObservers. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:40: HeapHashSet<Member<IntersectionObserver>> m_suspendedIntersectionObservers; As commented in another place, I'd drop this from this CL. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl:1: // Copyright 2015 The Chromium Authors. All rights reserved. Update the copyright of newly added files in this CL to 2016.
https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:47: } // namespace blink { On 2015/12/30 13:09:33, sof wrote: > nit: drop " {" (here & elsewhere in various spots.) Done, here and elsewhere. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:9: #include "wtf/RefPtr.h" On 2015/12/30 13:09:33, sof wrote: > not needed Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverCallback.h:10: #include "wtf/Vector.h" On 2015/12/30 13:09:33, sof wrote: > not needed. Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:70: void IntersectionObserverController::removeTrackedObserversForRoot(Element& root) On 2015/12/30 13:09:33, sof wrote: > const Element& possible? Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:72: HeapVector<Member<IntersectionObserver>> toRemove; On 2015/12/30 13:09:33, sof wrote: > IntersectionObserverVector? At the request of esprehn, I removed all container typedefs and inlined the full types. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:13: #include "wtf/RefCounted.h" On 2015/12/30 13:09:33, sof wrote: > Not needed. Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:17: class Document; On 2015/12/30 13:09:33, sof wrote: > not needed. Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:18: template<typename T> class Timer; On 2015/12/30 13:09:33, sof wrote: > shouldn't be needed here (now?) Done. https://codereview.chromium.org/1449623002/diff/300001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:49: #endif // IntersectionObservationRegistry_h On 2015/12/30 13:09:33, sof wrote: > doesn't quite match up. Fixed.
Here is the teardown scenario that led me to add dispose() and disconnect() and checkRootAndDetachIfNecessary() methods: The root element is deleted, but the IntersectionObserver is kept alive by living IntersectionObservations (which are kept alive by their target elements). On the next entry into the observer code (either running the intersection algorithm, or calling a javascript method on the observer), checkRootAndDetachIfNecessary() will notice that the root is gone, and it will disconnect all IntersectionObservations, which will break all hard references to Intersection* objects and allow them to be deleted the next time gc runs. In the non-oilpan world: ~ElementRareData() will call ElementIntersectionObserverData()::dispose(), which will synchronously do all the cleanup that allows IntersectionObserver and its IntersectionObservations to be deleted on the next gc. In this world, checkRootAndDetachIfNecessary is not needed. In the oilpan word: we can't do that cleanup in the root element destructor, so maybe use registerWeakMember for this? checkRootAndDetachIfNecessary() is a best-effort band-aid until this issue is resolved. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5120: void Document::tasksWereSuspended() On 2016/01/02 13:47:39, haraken wrote: > > Don't you need to suspend the observers when the document gets suspended? > > The only customer of suspend/resume is Devtools and a couple of unpopular > features. I'd propose not supporting suspend/resume in this CL (i.e. remove > IntersectionObserverController::m_suspendedIntersectionObservers). We can > support it in a follow-up CL. As I understand it, "suspended" in this case means "javascript cannot run." It's not clear to me that the observers should stop generating notifications in this circumstance; in this case, I am following the example of MutationObserver, which continues to generate notifications while tasks are suspended, but delays running the callbacks until after tasks have been resumed. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Element.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:110: #include "core/layout/LayoutPart.h" On 2016/01/02 13:47:40, haraken wrote: > > These #includes wouldn't be necessary. Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1463: rareData->ensureIntersectionObserverData().activateValidIntersectionObservers(*this); On 2016/01/02 13:47:40, haraken wrote: > > Nit: It looks a bit strange to call ensureXXX after checking XXX. > > if (ElementIntersectionObserverData* data = > rareData->intersectionObserverData()) > data->...; Fixed. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Element.cpp:1544: data->ensureIntersectionObserverData().deactivateAllIntersectionObservers(*this); On 2016/01/02 13:47:40, haraken wrote: > > Ditto. Fixed. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; On 2016/01/02 13:47:40, haraken wrote: > > I think this hash map is key for getting rid of checkRootAndDetachIfNeeded(). > > I don't fully understand why this hash map is needed. > > - (As I commented in the previous review,) this hash map is adding a strong > reference from the Observer to the Observation (whose target element is this > element). We won't want to have the strong reference because the Observer > shouldn't keep alive the Observation. I don't understand this comment. How does this add a strong reference from Observer to Observation? Rather it adds a strong reference from target to Observer and a strong reference from target to Observation. The target needs to hold a strong reference to the Observation, because that's the only thing keeping the Observation alive; nothing else holds a strong reference to the Observation. The Observation needs to hold a strong reference to the Observer, to keep the Observer alive when there are no more javascript references to the Observer; for example, this code: new IntersectionObserver().observe(target); ... should work, i.e., the observer should stay alive as long as it has an active observation. In m_intersectionObservations, the strong reference from target to IntersectionObserver (which is the hash key) is just an implementation detail, it does not change the lifetime or ownership model; it's just the transitive closure of target=>IntersectionObservation=>IntersectionObserver, which must exist. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:46: WeakPtrWillBeMember<Element> m_target; On 2016/01/02 13:47:40, haraken wrote: > > Doesn't this need to be a WeakMember? > > According to the design document > (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...), > observation => target needs to be weak. > > (Sorry, in the previous review I asked you to use a Member, not a WeakMember. I > now understand the lifetime relationship and I think a WeakMember is a right > solution.) Actually, I agreed with your previous comment :) This *can* be a WeakMember, but it need not be. If the only remaining reference to the target element is the strong reference from IntersectionObservation, then the garbage collector should detect the reference cycle and do the right thing. There are no other strong references to IntersectionObservation in the code. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:236: toDisconnect.append(observation); On 2016/01/02 13:47:40, haraken wrote: > > Use copyToVector. Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:284: bool IntersectionObserver::shouldBeSuspended() const On 2016/01/02 13:47:40, haraken wrote: > > To reduce the complexity of this CL, I'd propose not supporting resume/suspend > in this CL. I'm a bit concerned that something could go very wrong if the notification callbacks run while the execution context is suspended. As I mentioned before, this code was cargo-culted from MutationObserver, so I'd like to check with adamk before removing it. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:24: m_timer.startOneShot(0, BLINK_FROM_HERE); On 2016/01/02 13:47:41, haraken wrote: > > It's better to check m_timer.isActive() before starting a new shot. Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:80: void IntersectionObserverController::dispose() On 2016/01/02 13:47:41, haraken wrote: > > This dispose() shouldn't be necessary even in non-Oilpan. Let's remove it. Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.h:39: HeapHashSet<Member<IntersectionObserver>> m_activeIntersectionObservers; On 2016/01/02 13:47:41, haraken wrote: > > I'd rename this to m_pendingIntersectionObservers. "active" is confusing with > the active/inactive state of the IntersectionObservers. > Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverInit.idl:1: // Copyright 2015 The Chromium Authors. All rights reserved. On 2016/01/02 13:47:41, haraken wrote: > > Update the copyright of newly added files in this CL to 2016. Done, here and elsewhere.
On 2016/01/02 19:18:34, szager1 wrote: > Here is the teardown scenario that led me to add dispose() and disconnect() and > checkRootAndDetachIfNecessary() methods: > > The root element is deleted, but the IntersectionObserver is kept alive by > living IntersectionObservations (which are kept alive by their target elements). > > On the next entry into the observer code (either running the intersection > algorithm, or calling a javascript method on the observer), > checkRootAndDetachIfNecessary() will notice that the root is gone, and it will > disconnect all IntersectionObservations, which will break all hard references to > Intersection* objects and allow them to be deleted the next time gc runs. > > In the non-oilpan world: ~ElementRareData() will call > ElementIntersectionObserverData()::dispose(), which will synchronously do all > the cleanup that allows IntersectionObserver and its IntersectionObservations to > be deleted on the next gc. In this world, checkRootAndDetachIfNecessary is not > needed. > > In the oilpan word: we can't do that cleanup in the root element destructor, so > maybe use registerWeakMember for this? checkRootAndDetachIfNecessary() is a > best-effort band-aid until this issue is resolved. > Yes, a right fix would be using registerWeakMember for IntersectoinObsever::m_root. In the registerWeakMember's callback (which is called when the root element is gone but the Observer is still alive), you can call IntersectionObservation::disconnect. Note that the weak callback won't be called when the root element and the Observer die at the same time. But in that case we don't need to call IntersectionObvervation::disconnect, so that wouldn't be an issue here. =============== Also, I propose cleaning up the disconnection logic more. As commented in the previous review, I think it's easy to remove the following methods. They are just not necessary (if I'm understanding the lifetime relationship). - IntersectionObserver::dispose() - IntersectionObserver::disconnect() Also see my comment on ElementIntersectionObserverData::removeObservation(). https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5120: void Document::tasksWereSuspended() On 2016/01/02 19:18:34, szager1 wrote: > On 2016/01/02 13:47:39, haraken wrote: > > > > Don't you need to suspend the observers when the document gets suspended? > > > > The only customer of suspend/resume is Devtools and a couple of unpopular > > features. I'd propose not supporting suspend/resume in this CL (i.e. remove > > IntersectionObserverController::m_suspendedIntersectionObservers). We can > > support it in a follow-up CL. > > As I understand it, "suspended" in this case means "javascript cannot run." > It's not clear to me that the observers should stop generating notifications in > this circumstance; in this case, I am following the example of MutationObserver, > which continues to generate notifications while tasks are suspended, but delays > running the callbacks until after tasks have been resumed. Yes, you're right. What I'm suggesting is just to remove code for suspend/resume in this CL and support it in a follow-up CL. That would be acceptable because IntersectionObserver is under a runtime flag. This CL is already way too huge. If you need to support the behavior for suspend/resume correctly, I think we need to address a couple of more issues (e.g., I think you need to suspend Observers in Document::tasksWereSuspended()). Let's address the complexity in a follow-up CL. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; On 2016/01/02 19:18:34, szager1 wrote: > On 2016/01/02 13:47:40, haraken wrote: > > > > I think this hash map is key for getting rid of checkRootAndDetachIfNeeded(). > > > > I don't fully understand why this hash map is needed. > > > > - (As I commented in the previous review,) this hash map is adding a strong > > reference from the Observer to the Observation (whose target element is this > > element). We won't want to have the strong reference because the Observer > > shouldn't keep alive the Observation. > > I don't understand this comment. How does this add a strong reference from > Observer to Observation? Rather it adds a strong reference from target to > Observer and a strong reference from target to Observation. > > The target needs to hold a strong reference to the Observation, because that's > the only thing keeping the Observation alive; nothing else holds a strong > reference to the Observation. > > The Observation needs to hold a strong reference to the Observer, to keep the > Observer alive when there are no more javascript references to the Observer; for > example, this code: > > new IntersectionObserver().observe(target); > > ... should work, i.e., the observer should stay alive as long as it has an > active observation. > > In m_intersectionObservations, the strong reference from target to > IntersectionObserver (which is the hash key) is just an implementation detail, > it does not change the lifetime or ownership model; it's just the transitive > closure of target=>IntersectionObservation=>IntersectionObserver, which must > exist. Yeah, I was confused. You're right. This may not be a big issue for removing checkRootAndDetachIfNecessary(), but I still don't understand why the target element needs to keep a direct strong reference to the Observer. Given that the Observation already holds a strong reference to the Observer, the target element just needs to hold a strong reference to the Obseravation, doesn't it? (i.e., HeapHashSet<Member<IntersectionObvervation>> is enough.) https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:46: WeakPtrWillBeMember<Element> m_target; On 2016/01/02 19:18:34, szager1 wrote: > On 2016/01/02 13:47:40, haraken wrote: > > > > Doesn't this need to be a WeakMember? > > > > According to the design document > > > (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...), > > observation => target needs to be weak. > > > > (Sorry, in the previous review I asked you to use a Member, not a WeakMember. > I > > now understand the lifetime relationship and I think a WeakMember is a right > > solution.) > > Actually, I agreed with your previous comment :) This *can* be a WeakMember, > but it need not be. If the only remaining reference to the target element is > the strong reference from IntersectionObservation, then the garbage collector > should detect the reference cycle and do the right thing. There are no other > strong references to IntersectionObservation in the code. Thanks, I understand. Then I'd prefer using a WeakMember because it is a bit fragile to assume that there is no other strong references to the Observation even though it is true at the moment. https://codereview.chromium.org/1449623002/diff/340001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/340001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:39: void ElementIntersectionObserverData::removeObservation(IntersectionObserver& observer) Don't you need to remove the <Observer, Observation> entry from m_intersectionObservations? Either way, the disconnection logic around here looks pretty nasty. Observation::disconnect() calls ObserverData::removeObservation(), which in turn calls Observation::disconnect()...
I split out the suspend/resume and root margin features into separate patches. Here's the full list of dependent patches: https://codereview.chromium.org/1552213002 https://codereview.chromium.org/1559593002/ https://codereview.chromium.org/1545813002/ https://codereview.chromium.org/1548523002/ https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/Document.cpp (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/Document.cpp:5120: void Document::tasksWereSuspended() On 2016/01/03 16:34:20, haraken wrote: > On 2016/01/02 19:18:34, szager1 wrote: > > On 2016/01/02 13:47:39, haraken wrote: > > > > > > Don't you need to suspend the observers when the document gets suspended? > > > > > > The only customer of suspend/resume is Devtools and a couple of unpopular > > > features. I'd propose not supporting suspend/resume in this CL (i.e. remove > > > IntersectionObserverController::m_suspendedIntersectionObservers). We can > > > support it in a follow-up CL. > > > > As I understand it, "suspended" in this case means "javascript cannot run." > > It's not clear to me that the observers should stop generating notifications > in > > this circumstance; in this case, I am following the example of > MutationObserver, > > which continues to generate notifications while tasks are suspended, but > delays > > running the callbacks until after tasks have been resumed. > > Yes, you're right. What I'm suggesting is just to remove code for suspend/resume > in this CL and support it in a follow-up CL. That would be acceptable because > IntersectionObserver is under a runtime flag. This CL is already way too huge. > > If you need to support the behavior for suspend/resume correctly, I think we > need to address a couple of more issues (e.g., I think you need to suspend > Observers in Document::tasksWereSuspended()). Let's address the complexity in a > follow-up CL. > Done. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.h:40: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>> m_intersectionObservations; On 2016/01/03 16:34:20, haraken wrote: > On 2016/01/02 19:18:34, szager1 wrote: > > On 2016/01/02 13:47:40, haraken wrote: > > > > > > I think this hash map is key for getting rid of > checkRootAndDetachIfNeeded(). > > > > > > I don't fully understand why this hash map is needed. > > > > > > - (As I commented in the previous review,) this hash map is adding a strong > > > reference from the Observer to the Observation (whose target element is this > > > element). We won't want to have the strong reference because the Observer > > > shouldn't keep alive the Observation. > > > > I don't understand this comment. How does this add a strong reference from > > Observer to Observation? Rather it adds a strong reference from target to > > Observer and a strong reference from target to Observation. > > > > The target needs to hold a strong reference to the Observation, because that's > > the only thing keeping the Observation alive; nothing else holds a strong > > reference to the Observation. > > > > The Observation needs to hold a strong reference to the Observer, to keep the > > Observer alive when there are no more javascript references to the Observer; > for > > example, this code: > > > > new IntersectionObserver().observe(target); > > > > ... should work, i.e., the observer should stay alive as long as it has an > > active observation. > > > > In m_intersectionObservations, the strong reference from target to > > IntersectionObserver (which is the hash key) is just an implementation detail, > > it does not change the lifetime or ownership model; it's just the transitive > > closure of target=>IntersectionObservation=>IntersectionObserver, which must > > exist. > > Yeah, I was confused. You're right. > > This may not be a big issue for removing checkRootAndDetachIfNecessary(), but I > still don't understand why the target element needs to keep a direct strong > reference to the Observer. Given that the Observation already holds a strong > reference to the Observer, the target element just needs to hold a strong > reference to the Obseravation, doesn't it? (i.e., > HeapHashSet<Member<IntersectionObvervation>> is enough.) IntersectionObserver::observe() calls into m_intersectionObservations.contains(observer), to see if there's already an observer for this given observer/target pair. A previous version of this patch used custom hash_traits to make m_intersectionObservations a HashSet<IntersectionObservation> that supported contains(target, observer). esprehn objected that this use case did not merit the complexity of custom hash_traits, so I switched to HashMap<IntersectionObserver, IntersectionObservation>. https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/320001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:46: WeakPtrWillBeMember<Element> m_target; On 2016/01/03 16:34:20, haraken wrote: > On 2016/01/02 19:18:34, szager1 wrote: > > On 2016/01/02 13:47:40, haraken wrote: > > > > > > Doesn't this need to be a WeakMember? > > > > > > According to the design document > > > > > > (https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL...), > > > observation => target needs to be weak. > > > > > > (Sorry, in the previous review I asked you to use a Member, not a > WeakMember. > > I > > > now understand the lifetime relationship and I think a WeakMember is a right > > > solution.) > > > > Actually, I agreed with your previous comment :) This *can* be a WeakMember, > > but it need not be. If the only remaining reference to the target element is > > the strong reference from IntersectionObservation, then the garbage collector > > should detect the reference cycle and do the right thing. There are no other > > strong references to IntersectionObservation in the code. > > Thanks, I understand. Then I'd prefer using a WeakMember because it is a bit > fragile to assume that there is no other strong references to the Observation > even though it is true at the moment. > Done. https://codereview.chromium.org/1449623002/diff/340001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/340001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:39: void ElementIntersectionObserverData::removeObservation(IntersectionObserver& observer) On 2016/01/03 16:34:20, haraken wrote: > > Don't you need to remove the <Observer, Observation> entry from > m_intersectionObservations? > > Either way, the disconnection logic around here looks pretty nasty. > Observation::disconnect() calls ObserverData::removeObservation(), which in turn > calls Observation::disconnect()... Yes, good catch; I will work on simplifying the teardown logic.
Latest patch simplifies the teardown logic. PTAL, no bystanders please. I'm about ready to stab myself in the eye.
Looks much simpler. Here is the final round of comments. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:31: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>>::iterator i = m_intersectionObservations.find(&observer); Can we use auto& ? https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:79: observer->disconnect(); Why do you need to call disconnect() for Observers? Won't it be enough to call disconnect() for Observations? (i.e., the line 73 would be enough.) https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:188: bool IntersectionObserver::shouldBeSuspended() const Factor out this method to a follow-up CL. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:214: void IntersectionObserver::checkRootAndDetachIfNeeded() This implementation is clearly wrong in oilpan. We should add registerWeakMember to Observer::m_root and call the disconnect in the weak callback. We have agreed on fixing the issue in a follow-up CL. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:226: // TODO(szager): As a performance optimization, clear the hash tables before disconnecting Remove this TODO. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:41: for (auto& observer : m_trackedIntersectionObservers) Just to confirm: It is not possible that m_trackedIntersectionObservers is mutated while calling computeIntersectionObservations, right? (i.e., you don't need to copy the hash set to a vector before starting the iteration.) https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:36: RefPtrWillBeMember<Element> m_target; In both oilpan and non-oilpan, we have the following path: Observation =(strong)=> Observer =(strong)=> ObserverEntry =(strong)=> target element which means that we have a strong reference from the Observation to the target element. Is this expected? I guess it needs to be weak.
https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:31: HeapHashMap<Member<IntersectionObserver>, Member<IntersectionObservation>>::iterator i = m_intersectionObservations.find(&observer); On 2016/01/07 00:48:17, haraken wrote: > > Can we use auto& ? Done. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:79: observer->disconnect(); On 2016/01/07 00:48:17, haraken wrote: > > Why do you need to call disconnect() for Observers? Won't it be enough to call > disconnect() for Observations? (i.e., the line 73 would be enough.) The first section disconnects observations for which this element is target. The second section disconnects observers for which this element is root. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserver.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:188: bool IntersectionObserver::shouldBeSuspended() const On 2016/01/07 00:48:17, haraken wrote: > > Factor out this method to a follow-up CL. Done. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:214: void IntersectionObserver::checkRootAndDetachIfNeeded() On 2016/01/07 00:48:17, haraken wrote: > > This implementation is clearly wrong in oilpan. We should add registerWeakMember > to Observer::m_root and call the disconnect in the weak callback. > > We have agreed on fixing the issue in a follow-up CL. Acknowledged. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserver.cpp:226: // TODO(szager): As a performance optimization, clear the hash tables before disconnecting On 2016/01/07 00:48:17, haraken wrote: > > Remove this TODO. Done. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverController.cpp:41: for (auto& observer : m_trackedIntersectionObservers) On 2016/01/07 00:48:17, haraken wrote: > > Just to confirm: It is not possible that m_trackedIntersectionObservers is > mutated while calling computeIntersectionObservations, right? (i.e., you don't > need to copy the hash set to a vector before starting the iteration.) That's correct. Javascript cannot run during the iteration, and there's nothing in computeIntersectionObservations() that could alter m_trackedIntersectionObservers. https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObserverEntry.h:36: RefPtrWillBeMember<Element> m_target; On 2016/01/07 00:48:17, haraken wrote: > > In both oilpan and non-oilpan, we have the following path: > > Observation =(strong)=> Observer =(strong)=> ObserverEntry =(strong)=> target > element > > which means that we have a strong reference from the Observation to the target > element. Is this expected? I guess it needs to be weak. IntersectionObserverEntry is passed to javascript; I don't think we expose weak pointers to javascript, do we? Anyway, Observer =(strong)=> ObserverEntry is a short-lived reference; it only lasts until the notifications are delivered, at which time Observer drops its strong reference to ObserverEntry. I think it's appropriate that the target element is kept alive until the callback gets a chance to run.
bindings/, oilpan and the object lifetime relationships LGTM.
https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp (right): https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:79: observer->disconnect(); On 2016/01/07 07:12:22, szager1 wrote: > On 2016/01/07 00:48:17, haraken wrote: > > > > Why do you need to call disconnect() for Observers? Won't it be enough to call > > disconnect() for Observations? (i.e., the line 73 would be enough.) > > The first section disconnects observations for which this element is target. > The second section disconnects observers for which this element is root. Thanks. I understand the second one is necessary (this will be replaced with registerWeakMember in oilpan). But the first one looks a bit nasty. The first one is doing something like: - When the target element is destructed, it calls ElementIntersectionObserverData::dispose(). - ElementIntersectionObserverData::dispose() calls Observation::disconnect(). - The Observation::disconnect() removes the Observation from the ElementIntersectionObserverData of the target element that is currently being destructed. Is the first one necessary?
On 2016/01/07 07:38:13, haraken wrote: > https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... > File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp > (right): > > https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... > third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:79: > observer->disconnect(); > On 2016/01/07 07:12:22, szager1 wrote: > > On 2016/01/07 00:48:17, haraken wrote: > > > > > > Why do you need to call disconnect() for Observers? Won't it be enough to > call > > > disconnect() for Observations? (i.e., the line 73 would be enough.) > > > > The first section disconnects observations for which this element is target. > > The second section disconnects observers for which this element is root. > > Thanks. I understand the second one is necessary (this will be replaced with > registerWeakMember in oilpan). > > But the first one looks a bit nasty. The first one is doing something like: > > - When the target element is destructed, it calls > ElementIntersectionObserverData::dispose(). > - ElementIntersectionObserverData::dispose() calls Observation::disconnect(). > - The Observation::disconnect() removes the Observation from the > ElementIntersectionObserverData of the target element that is currently being > destructed. > > Is the first one necessary? It is necessary pre-oilpan, because Element is not garbage collected but IntersectionObservation is. There may be a gap in time between when the target Element is deleted (ref count drops to zero) and when the IntersectionObservation is deleted (gc). Calling Observation::disconnect() is meant to ensure that the Observation is not incorrectly used during that time.
On 2016/01/07 09:56:21, szager1 wrote: > On 2016/01/07 07:38:13, haraken wrote: > > > https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... > > File third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp > > (right): > > > > > https://codereview.chromium.org/1449623002/diff/400001/third_party/WebKit/Sou... > > third_party/WebKit/Source/core/dom/ElementIntersectionObserverData.cpp:79: > > observer->disconnect(); > > On 2016/01/07 07:12:22, szager1 wrote: > > > On 2016/01/07 00:48:17, haraken wrote: > > > > > > > > Why do you need to call disconnect() for Observers? Won't it be enough to > > call > > > > disconnect() for Observations? (i.e., the line 73 would be enough.) > > > > > > The first section disconnects observations for which this element is target. > > > > The second section disconnects observers for which this element is root. > > > > Thanks. I understand the second one is necessary (this will be replaced with > > registerWeakMember in oilpan). > > > > But the first one looks a bit nasty. The first one is doing something like: > > > > - When the target element is destructed, it calls > > ElementIntersectionObserverData::dispose(). > > - ElementIntersectionObserverData::dispose() calls Observation::disconnect(). > > - The Observation::disconnect() removes the Observation from the > > ElementIntersectionObserverData of the target element that is currently being > > destructed. > > > > Is the first one necessary? > > It is necessary pre-oilpan, because Element is not garbage collected but > IntersectionObservation is. There may be a gap in time between when the target > Element is deleted (ref count drops to zero) and when the > IntersectionObservation is deleted (gc). Calling Observation::disconnect() is > meant to ensure that the Observation is not incorrectly used during that time. Hmm. Observation::disconnect is doing the following three things: 1) m_target->ensureIntersectionObserverData().removeObservation 2) m_target.clear() 3) m_observer.clear() 1) wouldn't be needed because the IntersectionObserverData is currently being destructed. 2) wouldn't be needed because m_target is auto-cleared when the target Element gets destructed (and it is currently being destructed). 3) wouldn't be needed because both the Observation and the Observer are on oilpan's heap. So I don't think you need to call observation->disconnect(). This wouldn't be a big deal but it looks strange that Element's destructor calls observation->disconnect, which tries to clear things in the element that is being destructed.
On 2016/01/07 10:50:23, haraken wrote: > > Hmm. Observation::disconnect is doing the following three things: > > 1) m_target->ensureIntersectionObserverData().removeObservation > 2) m_target.clear() > 3) m_observer.clear() > > 1) wouldn't be needed because the IntersectionObserverData is currently being > destructed. > > 2) wouldn't be needed because m_target is auto-cleared when the target Element > gets destructed (and it is currently being destructed). > > 3) wouldn't be needed because both the Observation and the Observer are on > oilpan's heap. > > So I don't think you need to call observation->disconnect(). This wouldn't be a > big deal but it looks strange that Element's destructor calls > observation->disconnect, which tries to clear things in the element that is > being destructed. This was part of my attempt to simplify the teardown logic. ObserverData::dispose() is not the only caller to Observation::disconnect(). Thinking about this more, I think the teardown logic needs to be significantly more complex, primarily to cope with these two factors: - Pre-oilpan, there will be some delay between the time when DOM objects are deleted and when the next gc runs. When root or target Elements get deleted, we have to disconnect strong references that keep Observer and Observation objects alive, and we have to be careful that orphaned Observer and Observation objects are not misused before they are deleted during the next gc. - Post-oilpan, we'll need to use registerWeakMember to disconnect the strong references, but because the processing happens during gc, we can't modify HeapHash collections. There are three distinct teardown scenarios for Observations, and each one needs to be handled differently: 1) Observer::unobserve(target). In this case, no DOM objects are getting deleted and gc is not happening, so the Observation has to be explicitly unlinked from the target Element and the Observer. Out of an abundance of caution, I think we should break all links to the Observation to prevent it from being used incorrectly before it gets deleted on the next gc. 2) Target element gets deleted. 2.1) Pre-oilpan: the teardown does not happen during gc. Clear the strong reference Observation => Observer, and also clear the weak reference Observer => Observation, to prevent the Observation from being used incorrectly before it gets deleted during the next gc. 2.2) Post-oilpan: Observation will be deleted during the same gc, no additional teardown needed. 3) Root element gets deleted. In this case, the strong references target => Observation and Observation => Observer must be explicitly broken. 3.1) Pre-oilpan: the teardown happens synchronously in ObserverData::dispose(). Because it's not happening during gc, it's safe to modify HeapHash collections. In addition to the strong references, probably a good idea to also break the weak references Observer => Observation and Observation => target, to prevent the disconnected objects from being used incorrectly before the next gc. 3.2) Post-oilpan: this happens during gc, during a registerWeakMember callback. Observation => Observer strong reference can be cleared, but target => Observation cannot be cleared, because it's a HeapHashMap entry. In the absence of some additional magic (expose HeapHash functionality to remove entries while disallowing it to rehash?), the Observations can't be deleted until their target Element is deleted. So, as you can see, the effort to simplify the teardown logic is probably misguided. I will now proceed to re-complex-ify the teardown logic.
On 2016/01/07 19:12:13, szager1 wrote: > > 3.2) Post-oilpan: this happens during gc, during a registerWeakMember callback. > Observation => Observer strong reference can be cleared, but target => > Observation cannot be cleared, because it's a HeapHashMap entry. In the absence > of some additional magic (expose HeapHash functionality to remove entries while > disallowing it to rehash?), the Observations can't be deleted until their target > Element is deleted. Kentaro, can you give some guidance on this? Looking at the HashTable code, it looks like removing something from the table will not cause the table to resize if Allocator::isAllocationAllowed() is false (which I assume is the case during gc): https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... If I'm interpreting that correctly, then it's no problem to clear the target => Observation link during the registerWeakMember callback, and all of this should work (although it will still be grossly complex).
On 2016/01/07 20:27:37, szager1 wrote: > On 2016/01/07 19:12:13, szager1 wrote: > > > > 3.2) Post-oilpan: this happens during gc, during a registerWeakMember > callback. > > Observation => Observer strong reference can be cleared, but target => > > Observation cannot be cleared, because it's a HeapHashMap entry. In the > absence > > of some additional magic (expose HeapHash functionality to remove entries > while > > disallowing it to rehash?), the Observations can't be deleted until their > target > > Element is deleted. > > Kentaro, can you give some guidance on this? Looking at the HashTable code, it > looks like removing something from the table will not cause the table to resize > if Allocator::isAllocationAllowed() is false (which I assume is the case during > gc): > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > If I'm interpreting that correctly, then it's no problem to clear the target => > Observation link during the registerWeakMember callback, and all of this should > work (although it will still be grossly complex). I would also like to suggest that any additional work on the teardown code be done in a subsequent CL. The current latest version of this patch will work correctly in the non-oilpan case, and the feature is behind a flag.
> I would also like to suggest that any additional work on the teardown code be > done in a subsequent CL. The current latest version of this patch will work > correctly in the non-oilpan case, and the feature is behind a flag. I think that's a good plan and fully support it.
On 2016/01/07 20:40:00, eae wrote: > > I would also like to suggest that any additional work on the teardown code be > > done in a subsequent CL. The current latest version of this patch will work > > correctly in the non-oilpan case, and the feature is behind a flag. > > I think that's a good plan and fully support it. Yes, that makes sense.
It would be nice to hear from esprehn and/or ojan, but if I don't, then I'll commit tomorrow with haraken's lgtm.
Overall, I'm okay with landing this CL with some incorrect tear-down logic in oilpan (we can fix it later), but the tear-down logic must be correct in non-oilpan. On 2016/01/07 19:12:13, szager1 wrote: > On 2016/01/07 10:50:23, haraken wrote: > > > > Hmm. Observation::disconnect is doing the following three things: > > > > 1) m_target->ensureIntersectionObserverData().removeObservation > > 2) m_target.clear() > > 3) m_observer.clear() > > > > 1) wouldn't be needed because the IntersectionObserverData is currently being > > destructed. > > > > 2) wouldn't be needed because m_target is auto-cleared when the target Element > > gets destructed (and it is currently being destructed). > > > > 3) wouldn't be needed because both the Observation and the Observer are on > > oilpan's heap. > > > > So I don't think you need to call observation->disconnect(). This wouldn't be > a > > big deal but it looks strange that Element's destructor calls > > observation->disconnect, which tries to clear things in the element that is > > being destructed. > > > This was part of my attempt to simplify the teardown logic. > ObserverData::dispose() is not the only caller to Observation::disconnect(). > > Thinking about this more, I think the teardown logic needs to be significantly > more complex, primarily to cope with these two factors: I don't understand why you need to add more complexity to the tear down logic in oilpan. I think PS22 will work (although I don't think observation->disconnect() is needed). > > - Pre-oilpan, there will be some delay between the time when DOM objects are > deleted and when the next gc runs. When root or target Elements get deleted, we > have to disconnect strong references that keep Observer and Observation objects > alive, and we have to be careful that orphaned Observer and Observation objects > are not misused before they are deleted during the next gc. If we remove observation->disconnect() from ElementIntersectionObserverData::dispose() in PS22, in what scenario do we end up with using a stale Observation? > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the strong > references, but because the processing happens during gc, we can't modify > HeapHash collections. This issue is easily fixable. We can just disallow HashTable's rehashing while processing weak callbacks. I'll fix it. > > There are three distinct teardown scenarios for Observations, and each one needs > to be handled differently: > > 1) Observer::unobserve(target). In this case, no DOM objects are getting > deleted and gc is not happening, so the Observation has to be explicitly > unlinked from the target Element and the Observer. Out of an abundance of > caution, I think we should break all links to the Observation to prevent it from > being used incorrectly before it gets deleted on the next gc. > > 2) Target element gets deleted. > > 2.1) Pre-oilpan: the teardown does not happen during gc. Clear the strong > reference Observation => Observer, and also clear the weak reference Observer => > Observation, to prevent the Observation from being used incorrectly before it > gets deleted during the next gc. > > 2.2) Post-oilpan: Observation will be deleted during the same gc, no additional > teardown needed. > > 3) Root element gets deleted. In this case, the strong references target => > Observation and Observation => Observer must be explicitly broken. > > 3.1) Pre-oilpan: the teardown happens synchronously in ObserverData::dispose(). > Because it's not happening during gc, it's safe to modify HeapHash collections. > In addition to the strong references, probably a good idea to also break the > weak references Observer => Observation and Observation => target, to prevent > the disconnected objects from being used incorrectly before the next gc. > > 3.2) Post-oilpan: this happens during gc, during a registerWeakMember callback. > Observation => Observer strong reference can be cleared, but target => > Observation cannot be cleared, because it's a HeapHashMap entry. In the absence > of some additional magic (expose HeapHash functionality to remove entries while > disallowing it to rehash?), the Observations can't be deleted until their target > Element is deleted. > > > So, as you can see, the effort to simplify the teardown logic is probably > misguided. I will now proceed to re-complex-ify the teardown logic.
On 2016/01/08 00:54:30, haraken wrote: > > If we remove observation->disconnect() from > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we end > up with using a stale Observation? - Target Element is deleted, but IntersectionObservation will survive until the next gc. - Intersection algorithm runs before the next gc. - IntersectionObserver still points to the IntersectionObservation, so it call observation->computeIntersectionObservations() > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the strong > > references, but because the processing happens during gc, we can't modify > > HeapHash collections. > > This issue is easily fixable. We can just disallow HashTable's rehashing while > processing weak callbacks. I'll fix it. That will be very helpful, thank you.
On 2016/01/08 01:16:14, szager1 wrote: > On 2016/01/08 00:54:30, haraken wrote: > > > > If we remove observation->disconnect() from > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we end > > up with using a stale Observation? > > - Target Element is deleted, but IntersectionObservation will survive until the > next gc. > - Intersection algorithm runs before the next gc. > - IntersectionObserver still points to the IntersectionObservation, so it call > observation->computeIntersectionObservations() In that case, can't you just return early if the observation->m_target is already cleared? (The observation->m_target should have been cleared when the target element's destructor runs.) I think that's why we use a weak pointer for observation->m_target (i.e., Observation can outlive the target element). > > > > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the strong > > > references, but because the processing happens during gc, we can't modify > > > HeapHash collections. > > > > This issue is easily fixable. We can just disallow HashTable's rehashing while > > processing weak callbacks. I'll fix it. > > That will be very helpful, thank you.
On 2016/01/08 01:16:14, szager1 wrote: > On 2016/01/08 00:54:30, haraken wrote: > > > > If we remove observation->disconnect() from > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we end > > up with using a stale Observation? > > - Target Element is deleted, but IntersectionObservation will survive until the > next gc. > - Intersection algorithm runs before the next gc. > - IntersectionObserver still points to the IntersectionObservation, so it call > observation->computeIntersectionObservations() > > > > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the strong > > > references, but because the processing happens during gc, we can't modify > > > HeapHash collections. > > > > This issue is easily fixable. We can just disallow HashTable's rehashing while > > processing weak callbacks. I'll fix it. > > That will be very helpful, thank you. I noticed that this is already implemented. HashTable doesn't shrink while allocation is not allowed in Oilpan's GC. https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... So you can already remove an entry from a HeapHashTable during a weak callback.
On 2016/01/08 02:26:25, haraken wrote: > On 2016/01/08 01:16:14, szager1 wrote: > > On 2016/01/08 00:54:30, haraken wrote: > > > > > > If we remove observation->disconnect() from > > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we > end > > > up with using a stale Observation? > > > > - Target Element is deleted, but IntersectionObservation will survive until > the > > next gc. > > - Intersection algorithm runs before the next gc. > > - IntersectionObserver still points to the IntersectionObservation, so it call > > observation->computeIntersectionObservations() > > In that case, can't you just return early if the observation->m_target is > already cleared? (The observation->m_target should have been cleared when the > target element's destructor runs.) Well, yes, but that requires some extra defensive programming. This is similar to your argument for why Observation should have a weak pointer to the target Element: we could make it work fine for that to be a strong reference, but it would require some extra defensive programming to make sure we didn't introduce a strong reference to Observer somewhere else that would cause the target Element to leak. By the same reasoning, I'd like to do the thing that doesn't create pitfalls for future development.
On 2016/01/08 02:39:25, haraken wrote: > On 2016/01/08 01:16:14, szager1 wrote: > > On 2016/01/08 00:54:30, haraken wrote: > > > > > > If we remove observation->disconnect() from > > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we > end > > > up with using a stale Observation? > > > > - Target Element is deleted, but IntersectionObservation will survive until > the > > next gc. > > - Intersection algorithm runs before the next gc. > > - IntersectionObserver still points to the IntersectionObservation, so it call > > observation->computeIntersectionObservations() > > > > > > > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the > strong > > > > references, but because the processing happens during gc, we can't modify > > > > HeapHash collections. > > > > > > This issue is easily fixable. We can just disallow HashTable's rehashing > while > > > processing weak callbacks. I'll fix it. > > > > That will be very helpful, thank you. > > I noticed that this is already implemented. HashTable doesn't shrink while > allocation is not allowed in Oilpan's GC. > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > So you can already remove an entry from a HeapHashTable during a weak callback. Perhaps you missed comment #80 :) I believe we are in agreement.
On 2016/01/08 02:40:37, szager1 wrote: > On 2016/01/08 02:39:25, haraken wrote: > > On 2016/01/08 01:16:14, szager1 wrote: > > > On 2016/01/08 00:54:30, haraken wrote: > > > > > > > > If we remove observation->disconnect() from > > > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we > > end > > > > up with using a stale Observation? > > > > > > - Target Element is deleted, but IntersectionObservation will survive until > > the > > > next gc. > > > - Intersection algorithm runs before the next gc. > > > - IntersectionObserver still points to the IntersectionObservation, so it > call > > > observation->computeIntersectionObservations() > > > > > > > > > > > - Post-oilpan, we'll need to use registerWeakMember to disconnect the > > strong > > > > > references, but because the processing happens during gc, we can't > modify > > > > > HeapHash collections. > > > > > > > > This issue is easily fixable. We can just disallow HashTable's rehashing > > while > > > > processing weak callbacks. I'll fix it. > > > > > > That will be very helpful, thank you. > > > > I noticed that this is already implemented. HashTable doesn't shrink while > > allocation is not allowed in Oilpan's GC. > > > > > https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit... > > > > So you can already remove an entry from a HeapHashTable during a weak > callback. > > Perhaps you missed comment #80 :) I believe we are in agreement. I don't mean you need to address it in this CL. You can address the oilpan part in a follow-up.
On 2016/01/08 02:39:31, szager1 wrote: > On 2016/01/08 02:26:25, haraken wrote: > > On 2016/01/08 01:16:14, szager1 wrote: > > > On 2016/01/08 00:54:30, haraken wrote: > > > > > > > > If we remove observation->disconnect() from > > > > ElementIntersectionObserverData::dispose() in PS22, in what scenario do we > > end > > > > up with using a stale Observation? > > > > > > - Target Element is deleted, but IntersectionObservation will survive until > > the > > > next gc. > > > - Intersection algorithm runs before the next gc. > > > - IntersectionObserver still points to the IntersectionObservation, so it > call > > > observation->computeIntersectionObservations() > > > > In that case, can't you just return early if the observation->m_target is > > already cleared? (The observation->m_target should have been cleared when the > > target element's destructor runs.) > > Well, yes, but that requires some extra defensive programming. This is similar > to your argument for why Observation should have a weak pointer to the target > Element: we could make it work fine for that to be a strong reference, but it > would require some extra defensive programming to make sure we didn't introduce > a strong reference to Observer somewhere else that would cause the target > Element to leak. I'm not sure if the two are similar discussions. The basic programming contract is: a) Use a WeakPtr/WeakMember from A to B if A may outlive with B. b) If m_xxx is a WeakPtr/WeakMember, it means that m_xxx can be cleared anytime. You anyway have to insert 'if(!m_xxx)' check everywhere you use m_xxx. For the reason a), I suggested using a WeakMember for Observation::m_target. For the reason b), I'm suggesting implementing an early-return on observation->computeIntersectionObservations(). Does it make sense? > > By the same reasoning, I'd like to do the thing that doesn't create pitfalls for > future development.
Noticed this while skimming through the latest PS. https://codereview.chromium.org/1449623002/diff/440001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/440001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:9: #define IntersectionObservation_h The include guard should be the first thing.
https://codereview.chromium.org/1449623002/diff/440001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.h (right): https://codereview.chromium.org/1449623002/diff/440001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.h:9: #define IntersectionObservation_h On 2016/01/08 03:02:04, dcheng wrote: > The include guard should be the first thing. Fixed. This should probably be a lint error.
LGTM
The CQ bit was checked by szager@chromium.org
The patchset sent to the CQ was uploaded after l-g-t-m from haraken@chromium.org, eae@chromium.org Link to the patchset: https://codereview.chromium.org/1449623002/#ps500001 (title: "Add RuntimeEnabled flags to all idl's, fix test expectations.")
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/patch-status/1449623002/500001 View timeline at https://chromium-cq-status.appspot.com/patch-timeline/1449623002/500001
Message was sent while issue was closed.
Description was changed from ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... BUG=540528 ========== to ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... BUG=540528 ==========
Message was sent while issue was closed.
Committed patchset #26 (id:500001)
Message was sent while issue was closed.
Description was changed from ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... BUG=540528 ========== to ========== IntersectionObserver: second cut. All major features are implemented, a few TODO's, and a few tests. Design doc: https://docs.google.com/document/d/1hLK0eyT5_BzyNS4OkjsnoqqFQDYCbKfyBinj94OnL... BUG=540528 Committed: https://crrev.com/b5cff0041ec225f4eaac870e87d6189321754983 Cr-Commit-Position: refs/heads/master@{#368458} ==========
Message was sent while issue was closed.
Patchset 26 (id:??) landed as https://crrev.com/b5cff0041ec225f4eaac870e87d6189321754983 Cr-Commit-Position: refs/heads/master@{#368458}
Message was sent while issue was closed.
I don't think the tear-down logic for non-oilpan in PS26 is nice. Please address the comment in #92 in a follow-up.
Message was sent while issue was closed.
On 2016/01/09 04:11:54, haraken wrote: > I don't think the tear-down logic for non-oilpan in PS26 is nice. Please address > the comment in #92 in a follow-up. OK
Message was sent while issue was closed.
https://codereview.chromium.org/1449623002/diff/500001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/500001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:80: LayoutObject* targetLayoutObject = m_target->layoutObject(); Given that m_target is a WeakMember, it can be cleared anytime. You need to have an if(!m_target) check here and all other places that use m_target. As I mentioned before, the programming rule is: - If A may outlive B and you want to have a pointer from A to B, you should use a WeakPtr (in non-oilpan) or a WeakMember (in oilpan). - You must have a nullptr check before using the WeakPtr/WeakMember. This CL violates the rule in multiple places, so please fix them in a follow-up.
Message was sent while issue was closed.
https://codereview.chromium.org/1449623002/diff/500001/third_party/WebKit/Sou... File third_party/WebKit/Source/core/dom/IntersectionObservation.cpp (right): https://codereview.chromium.org/1449623002/diff/500001/third_party/WebKit/Sou... third_party/WebKit/Source/core/dom/IntersectionObservation.cpp:80: LayoutObject* targetLayoutObject = m_target->layoutObject(); On 2016/01/10 12:22:28, haraken wrote: > > Given that m_target is a WeakMember, it can be cleared anytime. You need to have > an if(!m_target) check here and all other places that use m_target. > > As I mentioned before, the programming rule is: > > - If A may outlive B and you want to have a pointer from A to B, you should use > a WeakPtr (in non-oilpan) or a WeakMember (in oilpan). > > - You must have a nullptr check before using the WeakPtr/WeakMember. > > This CL violates the rule in multiple places, so please fix them in a follow-up. All of the un-checked uses of m_target are in private methods that are only called through computeIntersectionObservations, which has a bool check for m_target up front. I will add ASSERT's. |