<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[235736] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/235736">235736</a></dd>
<dt>Author</dt> <dd>ajuma@chromium.org</dd>
<dt>Date</dt> <dd>2018-09-06 06:51:24 -0700 (Thu, 06 Sep 2018)</dd>
</dl>

<h3>Log Message</h3>
<pre>IntersectionObserver leaks documents
https://bugs.webkit.org/show_bug.cgi?id=189128

Reviewed by Simon Fraser.

Source/WebCore:

Currently, Documents own IntersectionObservers while IntersectionObservers own callbacks
that have strong references to Documents. To break this cycle, make Documents only have
weak pointers to IntersectionObservers. Instead, manage the lifetime of an
IntersectionObserver as an ActiveDOMObject, overriding hasPendingActivity to keep
the observer alive while there are ongoing observations.

However, there is a still a potential reference cycle. The callback keeps global
references alive, so if there's a global reference to the observer in JavaScript,
we have an observer->callback->observer cycle, keeping the callback (and hence the Document)
alive. To break this cycle, make IntersectionObserver release the callback when its
Document is stopped.

With these changes, there are no longer any leaks reported with run-webkit-tests --world-leaks
on LayoutTests/intersection-observer and LayoutTests/imported/w3c/web-platform-tests/intersection-observer.

Tests: intersection-observer/no-document-leak.html
       intersection-observer/observer-and-callback-without-js-references.html

* dom/Document.cpp:
(WebCore::Document::addIntersectionObserver):
(WebCore::Document::removeIntersectionObserver):
* dom/Document.h:
* dom/Element.cpp:
(WebCore::Element::didMoveToNewDocument):
* page/IntersectionObserver.cpp:
(WebCore::IntersectionObserver::IntersectionObserver):
(WebCore::IntersectionObserver::~IntersectionObserver):
(WebCore::IntersectionObserver::observe):
(WebCore::IntersectionObserver::rootDestroyed):
(WebCore::IntersectionObserver::createTimestamp const):
(WebCore::IntersectionObserver::notify):
(WebCore::IntersectionObserver::hasPendingActivity const):
(WebCore::IntersectionObserver::activeDOMObjectName const):
(WebCore::IntersectionObserver::canSuspendForDocumentSuspension const):
(WebCore::IntersectionObserver::stop):
* page/IntersectionObserver.h:
(WebCore::IntersectionObserver::trackingDocument const):
(WebCore::IntersectionObserver::trackingDocument): Deleted.
* page/IntersectionObserver.idl:

LayoutTests:

* intersection-observer/no-document-leak-expected.txt: Added.
* intersection-observer/no-document-leak.html: Added.
* intersection-observer/observer-and-callback-without-js-references-expected.txt: Added.
* intersection-observer/observer-and-callback-without-js-references.html: Added.
* intersection-observer/resources/no-document-leak-frame.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomElementcpp">trunk/Source/WebCore/dom/Element.cpp</a></li>
<li><a href="#trunkSourceWebCorepageIntersectionObservercpp">trunk/Source/WebCore/page/IntersectionObserver.cpp</a></li>
<li><a href="#trunkSourceWebCorepageIntersectionObserverh">trunk/Source/WebCore/page/IntersectionObserver.h</a></li>
<li><a href="#trunkSourceWebCorepageIntersectionObserveridl">trunk/Source/WebCore/page/IntersectionObserver.idl</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsintersectionobservernodocumentleakexpectedtxt">trunk/LayoutTests/intersection-observer/no-document-leak-expected.txt</a></li>
<li><a href="#trunkLayoutTestsintersectionobservernodocumentleakhtml">trunk/LayoutTests/intersection-observer/no-document-leak.html</a></li>
<li><a href="#trunkLayoutTestsintersectionobserverobserverandcallbackwithoutjsreferencesexpectedtxt">trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references-expected.txt</a></li>
<li><a href="#trunkLayoutTestsintersectionobserverobserverandcallbackwithoutjsreferenceshtml">trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references.html</a></li>
<li>trunk/LayoutTests/intersection-observer/resources/</li>
<li><a href="#trunkLayoutTestsintersectionobserverresourcesnodocumentleakframehtml">trunk/LayoutTests/intersection-observer/resources/no-document-leak-frame.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/LayoutTests/ChangeLog 2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2018-09-06  Ali Juma  <ajuma@chromium.org>
+
+        IntersectionObserver leaks documents
+        https://bugs.webkit.org/show_bug.cgi?id=189128
+
+        Reviewed by Simon Fraser.
+
+        * intersection-observer/no-document-leak-expected.txt: Added.
+        * intersection-observer/no-document-leak.html: Added.
+        * intersection-observer/observer-and-callback-without-js-references-expected.txt: Added.
+        * intersection-observer/observer-and-callback-without-js-references.html: Added.
+        * intersection-observer/resources/no-document-leak-frame.html: Added.
+
</ins><span class="cx"> 2018-09-05  Brent Fulgham  <bfulgham@apple.com>
</span><span class="cx"> 
</span><span class="cx">         The width of a nullptr TextRun should be zero
</span></span></pre></div>
<a id="trunkLayoutTestsintersectionobservernodocumentleakexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/intersection-observer/no-document-leak-expected.txt (0 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/intersection-observer/no-document-leak-expected.txt                            (rev 0)
+++ trunk/LayoutTests/intersection-observer/no-document-leak-expected.txt       2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Tests that using IntersectionObserver does not cause the document to get leaked.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Document did not leak
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsintersectionobservernodocumentleakhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/intersection-observer/no-document-leak.html (0 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/intersection-observer/no-document-leak.html                            (rev 0)
+++ trunk/LayoutTests/intersection-observer/no-document-leak.html       2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/js-test-pre.js"></script>
+</head>
+<body>
+<iframe id="testFrame" src="resources/no-document-leak-frame.html"></iframe>
+<script>
+description("Tests that using IntersectionObserver does not cause the document to get leaked.");
+window.jsTestIsAsync = true;
+
+function documentShouldDie(documentIdentifier)
+{
+    return new Promise(function(resolve, reject) {
+        handle = setInterval(function() {
+            gc();
+            if (internals && !internals.isDocumentAlive(documentIdentifier) && internals.numberOfIntersectionObservers(document) == 0) {
+                clearInterval(handle);
+                resolve();
+            }
+        }, 10);
+    });
+}
+
+var testFrame = document.getElementById("testFrame");
+testFrame.onload = function() {
+    let frameDocumentIdentifier = internals.documentIdentifier(testFrame.contentDocument);
+    testFrame.remove();
+    setTimeout(function() {
+        documentShouldDie(frameDocumentIdentifier).then(function() {
+            testPassed("Document did not leak");
+            finishJSTest();
+        });
+    });
+};
+</script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsintersectionobserverobserverandcallbackwithoutjsreferencesexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references-expected.txt (0 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references-expected.txt                         (rev 0)
+++ trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references-expected.txt    2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS IntersectionObserver callback fires even when the observer and callback have no JS references 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsintersectionobserverobserverandcallbackwithoutjsreferenceshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references.html (0 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references.html                         (rev 0)
+++ trunk/LayoutTests/intersection-observer/observer-and-callback-without-js-references.html    2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<!DOCTYPE html>
+<script src="../resources/gc.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<style>
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+<div id="target"></div>
+
+<script>
+var target = document.getElementById("target");
+
+async_test(function(t) {
+    new IntersectionObserver(function(changes) {
+        t.done();
+    }).observe(target);
+}, "IntersectionObserver callback fires even when the observer and callback have no JS references");
+
+gc();
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsintersectionobserverresourcesnodocumentleakframehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/intersection-observer/resources/no-document-leak-frame.html (0 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/intersection-observer/resources/no-document-leak-frame.html                            (rev 0)
+++ trunk/LayoutTests/intersection-observer/resources/no-document-leak-frame.html       2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+<!DOCTYPE html>
+<div id="target"></div>
+
+<script>
+var target = document.getElementById("target");
+var observer = new IntersectionObserver(function() { });
+observer.observe(target);
+</script>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/ChangeLog      2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -1,3 +1,50 @@
</span><ins>+2018-09-06  Ali Juma  <ajuma@chromium.org>
+
+        IntersectionObserver leaks documents
+        https://bugs.webkit.org/show_bug.cgi?id=189128
+
+        Reviewed by Simon Fraser.
+
+        Currently, Documents own IntersectionObservers while IntersectionObservers own callbacks
+        that have strong references to Documents. To break this cycle, make Documents only have
+        weak pointers to IntersectionObservers. Instead, manage the lifetime of an
+        IntersectionObserver as an ActiveDOMObject, overriding hasPendingActivity to keep
+        the observer alive while there are ongoing observations.
+
+        However, there is a still a potential reference cycle. The callback keeps global
+        references alive, so if there's a global reference to the observer in JavaScript,
+        we have an observer->callback->observer cycle, keeping the callback (and hence the Document)
+        alive. To break this cycle, make IntersectionObserver release the callback when its
+        Document is stopped.
+
+        With these changes, there are no longer any leaks reported with run-webkit-tests --world-leaks
+        on LayoutTests/intersection-observer and LayoutTests/imported/w3c/web-platform-tests/intersection-observer.
+
+        Tests: intersection-observer/no-document-leak.html
+               intersection-observer/observer-and-callback-without-js-references.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::addIntersectionObserver):
+        (WebCore::Document::removeIntersectionObserver):
+        * dom/Document.h:
+        * dom/Element.cpp:
+        (WebCore::Element::didMoveToNewDocument):
+        * page/IntersectionObserver.cpp:
+        (WebCore::IntersectionObserver::IntersectionObserver):
+        (WebCore::IntersectionObserver::~IntersectionObserver):
+        (WebCore::IntersectionObserver::observe):
+        (WebCore::IntersectionObserver::rootDestroyed):
+        (WebCore::IntersectionObserver::createTimestamp const):
+        (WebCore::IntersectionObserver::notify):
+        (WebCore::IntersectionObserver::hasPendingActivity const):
+        (WebCore::IntersectionObserver::activeDOMObjectName const):
+        (WebCore::IntersectionObserver::canSuspendForDocumentSuspension const):
+        (WebCore::IntersectionObserver::stop):
+        * page/IntersectionObserver.h:
+        (WebCore::IntersectionObserver::trackingDocument const):
+        (WebCore::IntersectionObserver::trackingDocument): Deleted.
+        * page/IntersectionObserver.idl:
+
</ins><span class="cx"> 2018-09-05  Zalan Bujtas  <zalan@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [LFC] Adapt to the new const WeakPtr<>
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp    2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/dom/Document.cpp       2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -7463,21 +7463,15 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(INTERSECTION_OBSERVER)
</span><del>-void Document::addIntersectionObserver(RefPtr<IntersectionObserver>&& observer)
</del><ins>+void Document::addIntersectionObserver(IntersectionObserver& observer)
</ins><span class="cx"> {
</span><del>-    ASSERT(m_intersectionObservers.find(observer) == notFound);
-    m_intersectionObservers.append(WTFMove(observer));
</del><ins>+    ASSERT(m_intersectionObservers.find(&observer) == notFound);
+    m_intersectionObservers.append(makeWeakPtr(&observer));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr<IntersectionObserver> Document::removeIntersectionObserver(IntersectionObserver& observer)
</del><ins>+void Document::removeIntersectionObserver(IntersectionObserver& observer)
</ins><span class="cx"> {
</span><del>-    RefPtr<IntersectionObserver> observerRef;
-    auto index = m_intersectionObservers.find(&observer);
-    if (index != notFound) {
-        observerRef = WTFMove(m_intersectionObservers[index]);
-        m_intersectionObservers.remove(index);
-    }
-    return observerRef;
</del><ins>+    m_intersectionObservers.removeFirst(&observer);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void computeIntersectionRects(FrameView& frameView, IntersectionObserver& observer, Element& target, FloatRect& absTargetRect, FloatRect& absIntersectionRect, FloatRect& absRootBounds)
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h      2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/dom/Document.h 2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -1369,8 +1369,8 @@
</span><span class="cx">     void removeViewportDependentPicture(HTMLPictureElement&);
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(INTERSECTION_OBSERVER)
</span><del>-    void addIntersectionObserver(RefPtr<IntersectionObserver>&&);
-    RefPtr<IntersectionObserver> removeIntersectionObserver(IntersectionObserver&);
</del><ins>+    void addIntersectionObserver(IntersectionObserver&);
+    void removeIntersectionObserver(IntersectionObserver&);
</ins><span class="cx">     unsigned numberOfIntersectionObservers() const { return m_intersectionObservers.size(); }
</span><span class="cx">     void updateIntersectionObservations();
</span><span class="cx">     void scheduleIntersectionObservationUpdate();
</span><span class="lines">@@ -1784,7 +1784,7 @@
</span><span class="cx">     HashSet<HTMLPictureElement*> m_viewportDependentPictures;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(INTERSECTION_OBSERVER)
</span><del>-    Vector<RefPtr<IntersectionObserver>> m_intersectionObservers;
</del><ins>+    Vector<WeakPtr<IntersectionObserver>> m_intersectionObservers;
</ins><span class="cx">     Vector<WeakPtr<IntersectionObserver>> m_intersectionObserversWithPendingNotifications;
</span><span class="cx"> 
</span><span class="cx">     // FIXME: Schedule intersection observation updates in a way that fits into the HTML
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.cpp (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.cpp     2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/dom/Element.cpp        2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -1699,8 +1699,10 @@
</span><span class="cx"> #if ENABLE(INTERSECTION_OBSERVER)
</span><span class="cx">     if (auto* observerData = intersectionObserverData()) {
</span><span class="cx">         for (auto observer : observerData->observers) {
</span><del>-            if (observer->hasObservationTargets())
-                newDocument.addIntersectionObserver(oldDocument.removeIntersectionObserver(*observer));
</del><ins>+            if (observer->hasObservationTargets()) {
+                oldDocument.removeIntersectionObserver(*observer);
+                newDocument.addIntersectionObserver(*observer);
+            }
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorepageIntersectionObservercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/IntersectionObserver.cpp (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/IntersectionObserver.cpp       2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/page/IntersectionObserver.cpp  2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -105,7 +105,8 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionObserverCallback>&& callback, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
</span><del>-    : m_root(root)
</del><ins>+    : ActiveDOMObject(downcast<Document>(callback->scriptExecutionContext()))
+    , m_root(root)
</ins><span class="cx">     , m_rootMargin(WTFMove(parsedRootMargin))
</span><span class="cx">     , m_thresholds(WTFMove(thresholds))
</span><span class="cx">     , m_callback(WTFMove(callback))
</span><span class="lines">@@ -117,6 +118,7 @@
</span><span class="cx">         m_implicitRootDocument = makeWeakPtr(frame->mainFrame().document());
</span><span class="cx"> 
</span><span class="cx">     std::sort(m_thresholds.begin(), m_thresholds.end());
</span><ins>+    suspendIfNeeded();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> IntersectionObserver::~IntersectionObserver()
</span><span class="lines">@@ -123,7 +125,7 @@
</span><span class="cx"> {
</span><span class="cx">     if (m_root)
</span><span class="cx">         m_root->intersectionObserverData()->observers.removeFirst(this);
</span><del>-    removeAllTargets();
</del><ins>+    disconnect();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> String IntersectionObserver::rootMargin() const
</span><span class="lines">@@ -145,7 +147,7 @@
</span><span class="cx"> 
</span><span class="cx"> void IntersectionObserver::observe(Element& target)
</span><span class="cx"> {
</span><del>-    if (!trackingDocument() || m_observationTargets.contains(&target))
</del><ins>+    if (!trackingDocument() || !m_callback || m_observationTargets.contains(&target))
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     target.ensureIntersectionObserverData().registrations.append({ makeWeakPtr(this), std::nullopt });
</span><span class="lines">@@ -153,7 +155,7 @@
</span><span class="cx">     m_observationTargets.append(&target);
</span><span class="cx">     auto* document = trackingDocument();
</span><span class="cx">     if (!hadObservationTargets)
</span><del>-        document->addIntersectionObserver(this);
</del><ins>+        document->addIntersectionObserver(*this);
</ins><span class="cx">     document->scheduleIntersectionObservationUpdate();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -219,16 +221,15 @@
</span><span class="cx"> void IntersectionObserver::rootDestroyed()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_root);
</span><del>-    auto& document = m_root->document();
</del><ins>+    disconnect();
</ins><span class="cx">     m_root = nullptr;
</span><del>-    if (hasObservationTargets()) {
-        removeAllTargets();
-        document.removeIntersectionObserver(*this);
-    }
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool IntersectionObserver::createTimestamp(DOMHighResTimeStamp& timestamp) const
</span><span class="cx"> {
</span><ins>+    if (!m_callback)
+        return false;
+
</ins><span class="cx">     auto* context = m_callback->scriptExecutionContext();
</span><span class="cx">     if (!context)
</span><span class="cx">         return false;
</span><span class="lines">@@ -250,12 +251,33 @@
</span><span class="cx"> 
</span><span class="cx"> void IntersectionObserver::notify()
</span><span class="cx"> {
</span><del>-    if (m_queuedEntries.isEmpty() || !m_callback->canInvokeCallback())
</del><ins>+    if (m_queuedEntries.isEmpty() || !m_callback || !m_callback->canInvokeCallback())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     m_callback->handleEvent(takeRecords(), *this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool IntersectionObserver::hasPendingActivity() const
+{
+    return hasObservationTargets() && trackingDocument();
+}
+
+const char* IntersectionObserver::activeDOMObjectName() const
+{
+    return "IntersectionObserver";
+}
+
+bool IntersectionObserver::canSuspendForDocumentSuspension() const
+{
+    return true;
+}
+
+void IntersectionObserver::stop()
+{
+    disconnect();
+    m_callback = nullptr;
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(INTERSECTION_OBSERVER)
</span></span></pre></div>
<a id="trunkSourceWebCorepageIntersectionObserverh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/IntersectionObserver.h (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/IntersectionObserver.h 2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/page/IntersectionObserver.h    2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(INTERSECTION_OBSERVER)
</span><span class="cx"> 
</span><ins>+#include "ActiveDOMObject.h"
</ins><span class="cx"> #include "IntersectionObserverCallback.h"
</span><span class="cx"> #include "IntersectionObserverEntry.h"
</span><span class="cx"> #include "LengthBox.h"
</span><span class="lines">@@ -47,8 +48,8 @@
</span><span class="cx"> 
</span><span class="cx"> struct IntersectionObserverData {
</span><span class="cx">     // IntersectionObservers for which the element that owns this IntersectionObserverData is the root.
</span><del>-    // An IntersectionObserver without any targets is only owned by JavaScript wrappers. An
-    // IntersectionObserver with at least one target is also owned by its trackingDocument.
</del><ins>+    // An IntersectionObserver is only owned by a JavaScript wrapper. ActiveDOMObject::hasPendingActivity
+    // is overridden to keep this wrapper alive while the observer has ongoing observations.
</ins><span class="cx">     Vector<WeakPtr<IntersectionObserver>> observers;
</span><span class="cx"> 
</span><span class="cx">     // IntersectionObserverRegistrations for which the element that owns this IntersectionObserverData is the target.
</span><span class="lines">@@ -55,7 +56,7 @@
</span><span class="cx">     Vector<IntersectionObserverRegistration> registrations;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-class IntersectionObserver : public RefCounted<IntersectionObserver>, public CanMakeWeakPtr<IntersectionObserver> {
</del><ins>+class IntersectionObserver : public RefCounted<IntersectionObserver>, public ActiveDOMObject, public CanMakeWeakPtr<IntersectionObserver> {
</ins><span class="cx"> public:
</span><span class="cx">     struct Init {
</span><span class="cx">         Element* root { nullptr };
</span><span class="lines">@@ -67,7 +68,7 @@
</span><span class="cx"> 
</span><span class="cx">     ~IntersectionObserver();
</span><span class="cx"> 
</span><del>-    Document* trackingDocument() { return m_root ? &m_root->document() : m_implicitRootDocument.get(); }
</del><ins>+    Document* trackingDocument() const { return m_root ? &m_root->document() : m_implicitRootDocument.get(); }
</ins><span class="cx"> 
</span><span class="cx">     Element* root() const { return m_root; }
</span><span class="cx">     String rootMargin() const;
</span><span class="lines">@@ -89,6 +90,12 @@
</span><span class="cx">     void appendQueuedEntry(Ref<IntersectionObserverEntry>&&);
</span><span class="cx">     void notify();
</span><span class="cx"> 
</span><ins>+    // ActiveDOMObject.
+    bool hasPendingActivity() const override;
+    const char* activeDOMObjectName() const override;
+    bool canSuspendForDocumentSuspension() const override;
+    void stop() override;
+
</ins><span class="cx"> private:
</span><span class="cx">     IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
</span><span class="cx"> 
</span><span class="lines">@@ -99,7 +106,7 @@
</span><span class="cx">     Element* m_root;
</span><span class="cx">     LengthBox m_rootMargin;
</span><span class="cx">     Vector<double> m_thresholds;
</span><del>-    Ref<IntersectionObserverCallback> m_callback;
</del><ins>+    RefPtr<IntersectionObserverCallback> m_callback;
</ins><span class="cx">     Vector<Element*> m_observationTargets;
</span><span class="cx">     Vector<Ref<IntersectionObserverEntry>> m_queuedEntries;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCorepageIntersectionObserveridl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/IntersectionObserver.idl (235735 => 235736)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/IntersectionObserver.idl       2018-09-06 13:40:56 UTC (rev 235735)
+++ trunk/Source/WebCore/page/IntersectionObserver.idl  2018-09-06 13:51:24 UTC (rev 235736)
</span><span class="lines">@@ -26,11 +26,11 @@
</span><span class="cx"> // https://wicg.github.io/IntersectionObserver/
</span><span class="cx"> 
</span><span class="cx"> [
</span><ins>+    ActiveDOMObject,
</ins><span class="cx">     Conditional=INTERSECTION_OBSERVER,
</span><span class="cx">     ConstructorCallWith=Document,
</span><span class="cx">     ConstructorMayThrowException,
</span><span class="cx">     Constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options),
</span><del>-    ImplementationLacksVTable,
</del><span class="cx">     EnabledAtRuntime=IntersectionObserver
</span><span class="cx"> ] interface IntersectionObserver {
</span><span class="cx">     readonly attribute Element? root;
</span></span></pre>
</div>
</div>

</body>
</html>