blob: bc78b418df739bfcb6131da4f96037084554eb17 [file] [log] [blame]
All of the IntersectionObserver tests feature the following idiom:
<script>
var observer = new IntersectionObserver(...)
function test_function() {
var entries = observer.takeRecords();
// Verify entries
}
onload = function() {
observer.observe(target);
requestAnimationFrame(() => { requestAnimationFrame(test_function) });
}
Subsequent steps in the test use a single RAF to give the observer a chance to
generate notifications, but the double RAF is required in the onload handler.
Here's the chain of events:
- onload
- observer.observe()
- First RAF handler is registered
- BeginFrame
- RAF handlers run
- Second RAF handler is registered
- UpdateAllLifecyclePhases
- IntersectionObserver generates notifications
- BeginFrame
- RAF handlers run
- Second RAF handler verifies observer notifications.
#-------------------------------------------------------------------------------
All of the IntersectionObserver tests are currently listed in LeakExpecations.
To avoid leaking DOM objects, the tests must ensure that all posted tasks have
run before exiting. IntersectionObserverController requests an idle callback
through the document's ScriptedIdleTaskController with a timeout of 100ms. That
callback must be allowed to run before the test finishes to avoid a leak.
ScriptedIdleTaskController posts two tasks to the WebScheduler: one to run at
the next idle time, and another to run when the timeout expires.
crbug.com/595155 explains that when one of those tasks runs, it does not release
its reference to the ScriptedIdleTaskController, which is needlessly kept alive
until the both tasks have fired. If a test exits before both tasks have fired,
it will have a DOM leak.
crbug.com/595152 explains that when running without the threaded compositor --
as the layout tests do -- idle tasks are never serviced. They will still run
when their timeout expires, but the idle task posted by
ScriptedIdleTaskController will never run.
Fixing the first bug means that requestIdleCallback will not leak as long as it
actually runs before exit. The second bug means that when running without the
threaded compositor, requestIdleCallback will only ever run when its timeout
expires.
The upshot of all of this is that the only way to ensure that
IntersectionObserver tests don't leak is to ensure that their idle tasks all run
before the tests exit, and those idle tasks will only run when their 100ms
timeout expires.
Note that all of this still holds when observer.takeRecords() is called before
the idle task runs. In that case, the idle task is not cancelled (because the
idle task needs to service all observers tracked by a given
IntersectionObserverController); when the idle task runs, it's simply a no-op.
There is no way in javascript to detect that such a no-op has occurred, but
using requestIdleCallback with a timeout of 100 should be guaranteed to run
after the IntersectionObserverController's idle task has run, as long as
ScriptedIdleTaskController honors first-in-first-out semantics.
At the time of writing, there is a patch out to add testRunner.runIdleTasks():
https://codereview.chromium.org/1806133002/
With that patch, the tests can be removed from LeakExpectations as long as they
end with:
if (window.testRunner)
testRunner.runIdleTasks(finishJSTest);