WebKit Bugzilla
Attachment 371183 Details for
Bug 197457
: The JS wrapper of target in an ResizeObserverEntry should not get collected
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-197457-20190603171105.patch (text/plain), 19.93 KB, created by
cathiechen
on 2019-06-03 02:11:08 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
cathiechen
Created:
2019-06-03 02:11:08 PDT
Size:
19.93 KB
patch
obsolete
>Subversion Revision: 245788 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index bb6aacac69a440eed3d117098bb1f977589fe5b7..1aefc2c1d50e38b1cac80972e411d8396a2ee8fa 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,33 @@ >+2019-05-27 Cathie Chen <cathiechen@igalia.com> >+ >+ JS wrapper of target in ResizeObserverEntry/ResizeObserver shouldn't get collected ahead >+ https://bugs.webkit.org/show_bug.cgi?id=197457 >+ >+ Reviewed by Ryosuke Niwa. >+ >+ Add JSCustomMarkFunction to make sure JS wrappers wouldn't be collected when JSResizeObserverEntry live. >+ >+ For ResizeObserver, if targets are removed, it will get fired for the last time. We also need to keep these JS >+ wrappers live. So add these targets to a GCReachableRef list once they're observed. >+ >+ Add element-leak.html to test the targets with `entry.target.myEntry = entry` could be released properly. >+ >+ Tests: resize-observer/element-leak.html >+ resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive.html >+ resize-observer/resize-observer-keeps-js-wrapper-of-target-alive.html >+ >+ * Sources.txt: >+ * WebCore.xcodeproj/project.pbxproj: >+ * bindings/js/JSResizeObserverEntryCustom.cpp: Added. >+ (WebCore::JSResizeObserverEntry::visitAdditionalChildren): >+ * page/ResizeObserver.cpp: >+ (WebCore::ResizeObserver::observe): >+ (WebCore::ResizeObserver::removeAllTargets): >+ (WebCore::ResizeObserver::removeObservation): >+ (WebCore::ResizeObserver::stop): >+ * page/ResizeObserver.h: >+ * page/ResizeObserverEntry.idl: >+ > 2019-05-27 Carlos Garcia Campos <cgarcia@igalia.com> > > Touch support is reported even when the device doesn't have a touch screen >diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt >index 2e2cfd14109813ab510017a122c1faf00201f33d..176b48e9df8b48b44865390dfa28a9bb5b3728d7 100644 >--- a/Source/WebCore/Sources.txt >+++ b/Source/WebCore/Sources.txt >@@ -532,6 +532,7 @@ bindings/js/JSPromiseRejectionEventCustom.cpp > bindings/js/JSReadableStreamSourceCustom.cpp > bindings/js/JSRemoteDOMWindowBase.cpp > bindings/js/JSRemoteDOMWindowCustom.cpp >+bindings/js/JSResizeObserverEntryCustom.cpp > bindings/js/JSSVGPathSegCustom.cpp > bindings/js/JSSVGViewSpecCustom.cpp > bindings/js/JSStyleSheetCustom.cpp >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index e95a8f930e4c38159c12ad63d3abf9aa3cf704b4..d8e867097c95f85bcc63213b705f2e6c4e297e12 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -8782,6 +8782,7 @@ > 585D6DFB1A15355600FA4F12 /* SimpleLineLayoutResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleLineLayoutResolver.cpp; sourceTree = "<group>"; }; > 585D6E011A1A792E00FA4F12 /* SimpleLineLayoutFlowContents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleLineLayoutFlowContents.cpp; sourceTree = "<group>"; }; > 585D6E021A1A792E00FA4F12 /* SimpleLineLayoutFlowContents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleLineLayoutFlowContents.h; sourceTree = "<group>"; }; >+ 5884FE5622813E2D0040AFF6 /* JSResizeObserverEntryCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSResizeObserverEntryCustom.cpp; sourceTree = "<group>"; }; > 589556EC18D4A44000764B03 /* BorderEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BorderEdge.h; sourceTree = "<group>"; }; > 58AEE2F318D4BCCF0022E7FE /* BorderEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BorderEdge.cpp; sourceTree = "<group>"; }; > 58B2F9EA2232D43B00938D63 /* ResizeObserverEntry.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ResizeObserverEntry.idl; sourceTree = "<group>"; }; >@@ -20672,6 +20673,7 @@ > CB38FD551CD21D5B00592A3F /* JSPerformanceEntryCustom.cpp */, > 833CF70F20DB3F5F00141BCC /* JSPerformanceObserverCustom.cpp */, > A4A69B8BB91B49D0A804C31D /* JSPromiseRejectionEventCustom.cpp */, >+ 5884FE5622813E2D0040AFF6 /* JSResizeObserverEntryCustom.cpp */, > 83F572941FA1066F003837BE /* JSServiceWorkerClientCustom.cpp */, > 460D19441FCE21DD00C3DB85 /* JSServiceWorkerGlobalScopeCustom.cpp */, > BC98A27C0C0C9950004BEBF7 /* JSStyleSheetCustom.cpp */, >diff --git a/Source/WebCore/bindings/js/JSResizeObserverEntryCustom.cpp b/Source/WebCore/bindings/js/JSResizeObserverEntryCustom.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..6b2a4c2d34127cdc6e51d9431d2e90252f8b2e69 >--- /dev/null >+++ b/Source/WebCore/bindings/js/JSResizeObserverEntryCustom.cpp >@@ -0,0 +1,39 @@ >+/* >+ * Copyright (C) 2019 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 "JSResizeObserverEntry.h" >+ >+#include "JSNodeCustom.h" >+ >+namespace WebCore { >+ >+void JSResizeObserverEntry::visitAdditionalChildren(JSC::SlotVisitor& visitor) >+{ >+ visitor.addOpaqueRoot(root(wrapped().target())); >+ visitor.addOpaqueRoot(wrapped().contentRect()); >+} >+ >+} >diff --git a/Source/WebCore/page/ResizeObserver.cpp b/Source/WebCore/page/ResizeObserver.cpp >index 9ff2b6ebc455462e436166f9d35367ea7fde8d7a..b9bd9bd9665c28da60dbe6461c0f03f5ae5ee5a4 100644 >--- a/Source/WebCore/page/ResizeObserver.cpp >+++ b/Source/WebCore/page/ResizeObserver.cpp >@@ -69,6 +69,7 @@ void ResizeObserver::observe(Element& target) > observerData.observers.append(makeWeakPtr(this)); > > m_observations.append(ResizeObservation::create(&target)); >+ m_pendingTargets.append(target); > > if (m_document) { > m_document->addResizeObserver(*this); >@@ -140,11 +141,17 @@ void ResizeObserver::removeAllTargets() > bool removed = removeTarget(*observation->target()); > ASSERT_UNUSED(removed, removed); > } >+ m_pendingTargets.clear(); >+ m_activeObservations.clear(); > m_observations.clear(); > } > > bool ResizeObserver::removeObservation(const Element& target) > { >+ m_pendingTargets.removeFirstMatching([&target](auto& pendingTarget) { >+ return pendingTarget.ptr() == ⌖ >+ }); >+ > m_activeObservations.removeFirstMatching([&target](auto& observation) { > return observation->target() == ⌖ > }); >@@ -173,8 +180,6 @@ void ResizeObserver::stop() > { > disconnect(); > m_callback = nullptr; >- m_observations.clear(); >- m_activeObservations.clear(); > } > > } // namespace WebCore >diff --git a/Source/WebCore/page/ResizeObserver.h b/Source/WebCore/page/ResizeObserver.h >index a1819609f6e0c3ee55bcdca3ac221fcaaa86b6e6..95b1c8b5be83d89b24979eaed9b9d4e90d3f4f87 100644 >--- a/Source/WebCore/page/ResizeObserver.h >+++ b/Source/WebCore/page/ResizeObserver.h >@@ -28,6 +28,7 @@ > #if ENABLE(RESIZE_OBSERVER) > > #include "ActiveDOMObject.h" >+#include "GCReachableRef.h" > #include "ResizeObservation.h" > #include "ResizeObserverCallback.h" > #include <wtf/RefCounted.h> >@@ -80,6 +81,7 @@ private: > Vector<Ref<ResizeObservation>> m_observations; > > Vector<Ref<ResizeObservation>> m_activeObservations; >+ Vector<GCReachableRef<Element>> m_pendingTargets; > bool m_hasSkippedObservations { false }; > }; > >diff --git a/Source/WebCore/page/ResizeObserverEntry.idl b/Source/WebCore/page/ResizeObserverEntry.idl >index b051bb57bec6925add74acd265442df05f79832a..72f85dcfce6bc0aa62a8980e25363102bd3492b0 100644 >--- a/Source/WebCore/page/ResizeObserverEntry.idl >+++ b/Source/WebCore/page/ResizeObserverEntry.idl >@@ -28,7 +28,8 @@ > [ > Conditional=RESIZE_OBSERVER, > ImplementationLacksVTable, >- EnabledBySetting=ResizeObserver >+ EnabledBySetting=ResizeObserver, >+ JSCustomMarkFunction > ] interface ResizeObserverEntry { > readonly attribute Element target; > readonly attribute DOMRectReadOnly contentRect; >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index b2d158a875b503821e4b6b198dce1eb813ec8fb9..740c80aa189a73bee41925e8e0532d884960c441 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,19 @@ >+2019-05-27 Cathie Chen <cathiechen@igalia.com> >+ >+ JS wrapper of target in ResizeObserverEntry/ResizeObserver shouldn't get collected ahead >+ https://bugs.webkit.org/show_bug.cgi?id=197457 >+ >+ Reviewed by Ryosuke Niwa. >+ >+ * platform/win/TestExpectations: >+ * resize-observer/element-leak-expected.txt: Added. >+ * resize-observer/element-leak.html: Added. >+ * resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive-expected.txt: Added. >+ * resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive.html: Added. >+ * resize-observer/resize-observer-keeps-js-wrapper-of-target-alive-expected.txt: Added. >+ * resize-observer/resize-observer-keeps-js-wrapper-of-target-alive.html: Added. >+ * resize-observer/resources/element-leak-frame.html: Added. >+ > 2019-05-27 Carlos Garcia Campos <cgarcia@igalia.com> > > Touch support is reported even when the device doesn't have a touch screen >diff --git a/LayoutTests/platform/win/TestExpectations b/LayoutTests/platform/win/TestExpectations >index 754a4a374627ded5ac33d7320687449370ee6a9e..a8ed5e570e6016e627d00cb774b3a736fdbc3616 100644 >--- a/LayoutTests/platform/win/TestExpectations >+++ b/LayoutTests/platform/win/TestExpectations >@@ -4392,3 +4392,7 @@ webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource.html [ Skip ] > webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource-iframe.html [ Skip ] > > webkit.org/b/198112 http/tests/security/showModalDialog-sync-cross-origin-page-load2.html [ Skip ] >+ >+# The removed elements couldn't be released properly in Win. >+# The relevant bug is https://bugs.webkit.org/show_bug.cgi?id=197908 >+resize-observer/element-leak.html [ Skip ] >diff --git a/LayoutTests/resize-observer/element-leak-expected.txt b/LayoutTests/resize-observer/element-leak-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..a25f3dbe173a59b991b2355765501ea4638d8971 >--- /dev/null >+++ b/LayoutTests/resize-observer/element-leak-expected.txt >@@ -0,0 +1,4 @@ >+ >+PASS ResizeObserver implemented >+PASS Test elements leak >+ >diff --git a/LayoutTests/resize-observer/element-leak.html b/LayoutTests/resize-observer/element-leak.html >new file mode 100644 >index 0000000000000000000000000000000000000000..316f2afcc6646e875424093d990f3c84f9b5eaba >--- /dev/null >+++ b/LayoutTests/resize-observer/element-leak.html >@@ -0,0 +1,42 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:ResizeObserverEnabled=true ] --> >+<html> >+<meta name="timeout" content="long"> >+<head> >+<script src="../resources/testharness.js"></script> >+<script src="../resources/testharnessreport.js"></script> >+<script src="../resources/gc.js"></script> >+</head> >+<body> >+<iframe id="testFrame" src="resources/element-leak-frame.html"></iframe> >+<script> >+ >+test(_ => { >+ assert_own_property(window, "ResizeObserver"); >+}, "ResizeObserver implemented"); >+ >+promise_test(async () => { >+ return new Promise(function(resolve, reject) { >+ window.addEventListener('message', event => { >+ switch(event.data) { >+ case 'Notified': >+ var testFrame = document.getElementById("testFrame"); >+ let frameDocumentIdentifier = internals.documentIdentifier(testFrame.contentDocument); >+ testFrame.remove(); >+ >+ handle = setInterval(function() { >+ gc(); >+ if (internals && !internals.isDocumentAlive(frameDocumentIdentifier)) { >+ clearInterval(handle); >+ resolve(); >+ } >+ }, 10); >+ break; >+ } >+ }, false); >+ setTimeout(() => reject("Test timed out"), 5000); >+ }); >+}, 'Test elements leak'); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive-expected.txt b/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..a5be9926a5a122dc0b0441c66c1ade67718b3e55 >--- /dev/null >+++ b/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive-expected.txt >@@ -0,0 +1,22 @@ >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+CONSOLE MESSAGE: ResizeObserver loop completed with undelivered notifications. >+This tests that JS wrappers of targets in an ResizeObserverEntry do not get collected. >+ >+PASS >+PASS >+PASS >+PASS >+PASS >+PASS >+PASS >+PASS >+PASS >+PASS >+ >diff --git a/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive.html b/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive.html >new file mode 100644 >index 0000000000000000000000000000000000000000..3cf844522af121afdd002ca7869e10282967f48a >--- /dev/null >+++ b/LayoutTests/resize-observer/resize-observer-entry-keeps-js-wrapper-of-target-alive.html >@@ -0,0 +1,60 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:ResizeObserverEnabled=true ] --> >+<html> >+<body> >+<p>This tests that JS wrappers of targets in an ResizeObserverEntry do not get collected.</p> >+<pre id="log"></pre> >+<script src="../resources/gc.js"></script> >+<script> >+ >+if (window.testRunner) >+ testRunner.dumpAsText(); >+ >+const targetCount = 5; >+const iterationCount = 10; >+ >+async function observe() { >+ for (let i = 0; i < targetCount; ++i) { >+ let target = document.createElement('div'); >+ target.myState = 'live'; >+ document.body.appendChild(target); >+ } >+ >+ return new Promise((resolve) => { >+ const observer = new ResizeObserver(entries => { >+ resolve(entries); >+ observer.disconnect(); >+ }); >+ document.querySelectorAll('div').forEach(target => observer.observe(target)); >+ }); >+} >+ >+function check(entries) { >+ let deadCount = 0; >+ for (const entry of entries) { >+ if (entry.target.myState != 'live') >+ deadCount++; >+ } >+ document.getElementById('log').textContent += (deadCount ? `FAIL - ${deadCount} targets lost JS wrappers` : 'PASS') + '\n'; >+} >+ >+async function runAll() { >+ if (window.testRunner) >+ testRunner.waitUntilDone(); >+ >+ for (let j = 0; j < iterationCount; ++j) { >+ const entries = await observe(); >+ document.querySelectorAll('div').forEach(target => target.remove()); >+ await Promise.resolve(); >+ gc(); >+ check(entries); >+ } >+ >+ if (window.testRunner) >+ testRunner.notifyDone(); >+} >+ >+runAll(); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive-expected.txt b/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..1fe5345c387b0760b2ebea7d982807bc400fd6d8 >--- /dev/null >+++ b/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive-expected.txt >@@ -0,0 +1,4 @@ >+This tests that JS wrappers of targets removed from document to be delivered to an resize observer do not get collected. >+ >+PASS >+ >diff --git a/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive.html b/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive.html >new file mode 100644 >index 0000000000000000000000000000000000000000..98677514fd46b943f4adb0d12702781d0319c72a >--- /dev/null >+++ b/LayoutTests/resize-observer/resize-observer-keeps-js-wrapper-of-target-alive.html >@@ -0,0 +1,55 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ experimental:ResizeObserverEnabled=true ] --> >+<html> >+<body> >+<p>This tests that JS wrappers of targets removed from document to be delivered to an resize observer do not get collected.</p> >+<pre id="log"></pre> >+<script src="../resources/gc.js"></script> >+<script> >+ >+if (window.testRunner) >+ testRunner.dumpAsText(); >+ >+const targetCount = 5; >+const iterationCount = 10; >+var deadCount = 0; >+ >+async function runAll() { >+ if (window.testRunner) >+ testRunner.waitUntilDone(); >+ >+ for (let i = 0; i < iterationCount; ++i) { >+ runTest(); >+ gc(); >+ await new Promise((resolve) => requestAnimationFrame(resolve)) >+ } >+ >+ document.getElementById('log').textContent = (deadCount ? `FAIL - ${deadCount} targets lost JS wrappers` : 'PASS') + '\n'; >+ >+ if (window.testRunner) >+ testRunner.notifyDone(); >+} >+ >+function runTest() { >+ document.querySelectorAll('div').forEach(target => target.remove()); >+ >+ for (let i = 0; i < targetCount; ++i) { >+ let target = document.createElement('div'); >+ target.myState = 'live'; >+ document.body.appendChild(target); >+ } >+ >+ document.querySelectorAll('div').forEach(target => observer.observe(target)); >+} >+ >+const observer = new ResizeObserver(entries => { >+ for (const entry of entries) { >+ if (entry.target.myState != 'live') >+ deadCount++; >+ } >+}); >+ >+runAll(); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/resize-observer/resources/element-leak-frame.html b/LayoutTests/resize-observer/resources/element-leak-frame.html >new file mode 100644 >index 0000000000000000000000000000000000000000..b162d44d424f3a009bb8f31ee350cd48504f4e10 >--- /dev/null >+++ b/LayoutTests/resize-observer/resources/element-leak-frame.html >@@ -0,0 +1,26 @@ >+<!DOCTYPE html> >+<body></body> >+<script src="../../resources/gc.js"></script> >+<script type="text/javascript"> >+ >+const targetCount = 1000; >+var resizeObserver = new ResizeObserver( entries => { >+ for (let entry of entries) >+ entry.target.myEntry = entry; >+ >+ resizeObserver.disconnect(); >+ document.querySelectorAll('div').forEach(target => target.remove()); >+ // Make sure targets be added to m_opaqueRoots. >+ gc(); >+ parent.postMessage('Notified', '*'); >+}); >+ >+for (let i = 0; i < targetCount; ++i) { >+ var target = document.createElement('div'); >+ document.body.appendChild(target); >+} >+ >+document.querySelectorAll('div').forEach(target => resizeObserver.observe(target)); >+ >+</script> >+</html> >\ No newline at end of file
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 197457
:
368670
|
369394
|
369426
|
369680
|
369681
|
369683
|
369738
|
369746
|
369752
|
369755
|
369756
|
369764
|
369833
|
369837
|
369840
|
369844
|
369845
|
369846
|
369847
|
369848
|
369854
|
369861
|
369862
|
369866
|
369870
|
369876
|
369877
|
369921
|
369922
|
369923
|
369925
|
369926
|
369927
|
369935
|
369942
|
370111
|
370681
|
371183
|
371186
|
371241