Diff
Modified: trunk/LayoutTests/ChangeLog (269140 => 269141)
--- trunk/LayoutTests/ChangeLog 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/LayoutTests/ChangeLog 2020-10-29 08:15:18 UTC (rev 269141)
@@ -1,3 +1,19 @@
+2020-10-29 Cathie Chen <[email protected]>
+
+ IntersectionObserverCallback leak
+ https://bugs.webkit.org/show_bug.cgi?id=218225
+
+ Reviewed by Ryosuke Niwa.
+
+ In intersection-observer-callback-after-gc.html, the callbacks with observed targets deleted should stay alive as long as observers are alive.
+ In intersection-observer-callback-leak.html, use `internals.numberOfLiveNodes` to check that the objects inside JSIntersectionObserverCallback
+ can be garbage collected.
+
+ * intersection-observer/intersection-observer-callback-after-gc-expected.txt: Added.
+ * intersection-observer/intersection-observer-callback-after-gc.html: Added.
+ * intersection-observer/intersection-observer-callback-leak-expected.txt: Added.
+ * intersection-observer/intersection-observer-callback-leak.html: Added.
+
2020-10-28 Julian Gonzalez <[email protected]>
Null dereference in CompositeEditCommand::cloneParagraphUnderNewElement() due to not checking for top of DOM tree
Added: trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc-expected.txt (0 => 269141)
--- trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc-expected.txt (rev 0)
+++ trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc-expected.txt 2020-10-29 08:15:18 UTC (rev 269141)
@@ -0,0 +1,3 @@
+
+PASS test: IntersectionObserverCallbacks stay alive if according observers is alive
+
Added: trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc.html (0 => 269141)
--- trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc.html (rev 0)
+++ trunk/LayoutTests/intersection-observer/intersection-observer-callback-after-gc.html 2020-10-29 08:15:18 UTC (rev 269141)
@@ -0,0 +1,57 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:IntersectionObserverEnabled=true ] -->
+<html>
+
+<head>
+ <script src=""
+ <script src=""
+ <script src=""
+</head>
+
+<body>
+ <div id="container"></div>
+ <script>
+ async function callbackSurvivesAfterGC() {
+ let test = async_test("test: IntersectionObserverCallbacks stay alive if according observers is alive");
+ const iterationCount = 10;
+ let deletedNodesCount = 0;
+ let callbackCountAfterDelete = 0;
+ let observers = [];
+ await new Promise((resolve, reject) => {
+ for (let j = 0; j < iterationCount; ++j) {
+ const div = document.createElement('div');
+ div.state = "toDelete";
+ container.appendChild(div);
+ let observer = new IntersectionObserver(entries => {
+ for (const entry of entries) {
+ if (entry.target.state == "toDelete") {
+ deletedNodesCount++;
+ container.removeChild(entry.target);
+ entry.target.state = "deleted";
+ if (deletedNodesCount == iterationCount)
+ resolve();
+ } else if (entry.target.state == "new") {
+ entry.target.state = "checked";
+ callbackCountAfterDelete++;
+ }
+ }
+ if (callbackCountAfterDelete == iterationCount)
+ test.done();
+ });
+ observer.observe(div);
+ observers[j] = observer;
+ }
+ });
+ gc();
+ for (let j = 0; j < iterationCount; ++j) {
+ const div = document.createElement('div');
+ div.state = "new";
+ container.appendChild(div);
+ observers[j].observe(div);
+ }
+ }
+ window._onload_ = callbackSurvivesAfterGC();
+ </script>
+
+</body>
+
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak-expected.txt (0 => 269141)
--- trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak-expected.txt (rev 0)
+++ trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak-expected.txt 2020-10-29 08:15:18 UTC (rev 269141)
@@ -0,0 +1,3 @@
+
+PASS test: Nodes inside callback should be garbage collected if no one refers to it
+
Added: trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak.html (0 => 269141)
--- trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak.html (rev 0)
+++ trunk/LayoutTests/intersection-observer/intersection-observer-callback-leak.html 2020-10-29 08:15:18 UTC (rev 269141)
@@ -0,0 +1,45 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:IntersectionObserverEnabled=true ] -->
+<html>
+
+<head>
+ <script src=""
+ <script src=""
+ <script src=""
+</head>
+
+<body>
+ <div id="container"></div>
+ <script>
+ function accessToObserverInCallback() {
+ let test = async_test("test: Nodes inside callback should be garbage collected if no one refers to it");
+ let initialNodesCount = internals.numberOfLiveNodes(document);
+ const iterationCount = 50;
+ let callbackCount = 0;
+ for (let j = 0; j < iterationCount; ++j) {
+ const div = document.createElement('div');
+ container.appendChild(div);
+ let observer = new IntersectionObserver(() => {
+ callbackCount++;
+ observer.disconnect();
+ container.removeChild(div);
+ if (callbackCount == iterationCount) {
+ window.requestAnimationFrame(() => {
+ gc();
+ test.step(() => {
+ let additionalNodesCount = internals.numberOfLiveNodes(document) - initialNodesCount;
+ assert_true(additionalNodesCount < iterationCount, 'Nodes inside callback should be collected.');
+ test.done();
+ });
+ });
+ }
+ });
+ observer.observe(div);
+ }
+ }
+
+ window._onload_ = accessToObserverInCallback();
+ </script>
+
+</body>
+
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (269140 => 269141)
--- trunk/Source/WebCore/ChangeLog 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/ChangeLog 2020-10-29 08:15:18 UTC (rev 269141)
@@ -1,3 +1,27 @@
+2020-10-29 Cathie Chen <[email protected]>
+
+ IntersectionObserverCallback leaks
+ https://bugs.webkit.org/show_bug.cgi?id=218225
+
+ Reviewed by Ryosuke Niwa.
+
+ Unless the page is unloaded, JSIntersectionObserverCallback and objects inside it can not be garbage collected properly.
+ To fix this, make IntersectionObserverCallback as a weak callback. To keep it alive, in JSIntersectionObserver::visitAdditionalChildren
+ add the callback to visitor.
+
+ Test: intersection-observer/intersection-observer-callback-leak.html
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * bindings/js/JSIntersectionObserverCustom.cpp: Copied from Source/WebCore/page/IntersectionObserverCallback.idl.
+ (WebCore::JSIntersectionObserver::visitAdditionalChildren):
+ * page/IntersectionObserver.h:
+ (WebCore::IntersectionObserver::callbackConcurrently):
+ * page/IntersectionObserver.idl:
+ * page/IntersectionObserverCallback.h:
+ (WebCore::IntersectionObserverCallback::hasCallback const):
+ * page/IntersectionObserverCallback.idl:
+
2020-10-28 Julian Gonzalez <[email protected]>
Null dereference in CompositeEditCommand::cloneParagraphUnderNewElement() due to not checking for top of DOM tree
Modified: trunk/Source/WebCore/Sources.txt (269140 => 269141)
--- trunk/Source/WebCore/Sources.txt 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/Sources.txt 2020-10-29 08:15:18 UTC (rev 269141)
@@ -545,6 +545,7 @@
bindings/js/JSIDBTransactionCustom.cpp
bindings/js/JSIDBSerializationGlobalObject.cpp
bindings/js/JSImageDataCustom.cpp
+bindings/js/JSIntersectionObserverCustom.cpp
bindings/js/JSIntersectionObserverEntryCustom.cpp
bindings/js/JSLazyEventListener.cpp
bindings/js/JSLocationCustom.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (269140 => 269141)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-10-29 08:15:18 UTC (rev 269141)
@@ -10012,6 +10012,7 @@
77A17AA312F28B2A004E02F6 /* JSOESVertexArrayObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSOESVertexArrayObject.h; sourceTree = "<group>"; };
77AAD6831ECFB66200BFA2D1 /* CredentialCreationOptions.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CredentialCreationOptions.idl; sourceTree = "<group>"; };
77AAD6851ECFBD3900BFA2D1 /* CredentialCreationOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CredentialCreationOptions.h; sourceTree = "<group>"; };
+ 5868C7D52546E0B300BF9DF3 /* JSIntersectionObserverCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSIntersectionObserverCustom.cpp; sourceTree = "<group>"; };
77C13F042165658A002D9C5F /* JSIntersectionObserverEntryCustom.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSIntersectionObserverEntryCustom.cpp; sourceTree = "<group>"; };
77CAAAEF1F2FC35000CB5C8D /* VisualViewport.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VisualViewport.idl; sourceTree = "<group>"; };
77D50FFA1ED4EC7800DA4C87 /* CredentialRequestOptions.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CredentialRequestOptions.idl; sourceTree = "<group>"; };
@@ -22363,6 +22364,7 @@
511EF2CE17F0FDF100E4FA16 /* JSIDBObjectStoreCustom.cpp */,
51E269321DD3BC43006B6A58 /* JSIDBTransactionCustom.cpp */,
A7D0318D0E93540300E24ACD /* JSImageDataCustom.cpp */,
+ 5868C7D52546E0B300BF9DF3 /* JSIntersectionObserverCustom.cpp */,
77C13F042165658A002D9C5F /* JSIntersectionObserverEntryCustom.cpp */,
CD0320152279F5BF00DD9F0B /* JSMediaCapabilitiesCustom.h */,
AD726FE716D9F204003A4E6D /* JSMediaListCustom.h */,
Copied: trunk/Source/WebCore/bindings/js/JSIntersectionObserverCustom.cpp (from rev 269137, trunk/Source/WebCore/page/IntersectionObserverCallback.idl) (0 => 269141)
--- trunk/Source/WebCore/bindings/js/JSIntersectionObserverCustom.cpp (rev 0)
+++ trunk/Source/WebCore/bindings/js/JSIntersectionObserverCustom.cpp 2020-10-29 08:15:18 UTC (rev 269141)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSIntersectionObserver.h"
+
+#include <_javascript_Core/JSCInlines.h>
+
+namespace WebCore {
+
+void JSIntersectionObserver::visitAdditionalChildren(JSC::SlotVisitor& visitor)
+{
+ if (auto* callback = wrapped().callbackConcurrently())
+ callback->visitJSFunction(visitor);
+}
+
+}
Modified: trunk/Source/WebCore/page/IntersectionObserver.h (269140 => 269141)
--- trunk/Source/WebCore/page/IntersectionObserver.h 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/page/IntersectionObserver.h 2020-10-29 08:15:18 UTC (rev 269141)
@@ -99,6 +99,8 @@
void appendQueuedEntry(Ref<IntersectionObserverEntry>&&);
void notify();
+ IntersectionObserverCallback* callbackConcurrently() { return m_callback.get(); }
+
private:
IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, ContainerNode* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
Modified: trunk/Source/WebCore/page/IntersectionObserver.idl (269140 => 269141)
--- trunk/Source/WebCore/page/IntersectionObserver.idl 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/page/IntersectionObserver.idl 2020-10-29 08:15:18 UTC (rev 269141)
@@ -29,7 +29,8 @@
ActiveDOMObject,
Conditional=INTERSECTION_OBSERVER,
EnabledBySetting=IntersectionObserver,
- Exposed=Window
+ Exposed=Window,
+ JSCustomMarkFunction,
] interface IntersectionObserver {
[CallWith=Document] constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options);
Modified: trunk/Source/WebCore/page/IntersectionObserverCallback.h (269140 => 269141)
--- trunk/Source/WebCore/page/IntersectionObserverCallback.h 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/page/IntersectionObserverCallback.h 2020-10-29 08:15:18 UTC (rev 269141)
@@ -41,6 +41,8 @@
public:
using ActiveDOMCallback::ActiveDOMCallback;
+ virtual bool hasCallback() const { return false; }
+
virtual CallbackResult<void> handleEvent(IntersectionObserver&, const Vector<Ref<IntersectionObserverEntry>>&, IntersectionObserver&) = 0;
};
Modified: trunk/Source/WebCore/page/IntersectionObserverCallback.idl (269140 => 269141)
--- trunk/Source/WebCore/page/IntersectionObserverCallback.idl 2020-10-29 06:31:36 UTC (rev 269140)
+++ trunk/Source/WebCore/page/IntersectionObserverCallback.idl 2020-10-29 08:15:18 UTC (rev 269141)
@@ -28,4 +28,5 @@
[
Conditional=INTERSECTION_OBSERVER,
CallbackThisObject=IntersectionObserver,
+ IsWeakCallback,
] callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);