Commit f65c1e27 authored by haozhe's avatar haozhe Committed by Commit Bot

getAnimations from the Document instead of from Timeline

Per the spec, Document::getAnimations() must report all Animations whose
target effect is the given document.

Spec:
https://drafts.csswg.org/web-animations/#extensions-to-the-documentorshadowroot-interface-mixin

Bug: 828585
Change-Id: I92a5cab69d0b9869fea0238e2cfb8e98d995a3b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2024539
Commit-Queue: Hao Sheng <haozhes@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarKevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#741473}
parent 8d9325b5
......@@ -40,6 +40,8 @@
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/page_animator.h"
namespace blink {
......@@ -121,8 +123,29 @@ HeapVector<Member<Animation>> DocumentAnimations::getAnimations() {
// This method implements the Document::getAnimations method defined in the
// web-animations-1 spec.
// https://drafts.csswg.org/web-animations-1/#dom-document-getanimations
// TODO(crbug.com/1046916): refactoring work to create a shared implementation
// of getAnimations for Documents and ShadowRoots.
document_->UpdateStyleAndLayoutTree();
HeapVector<Member<Animation>> animations;
if (document_->GetPage())
animations = document_->GetPage()->Animator().GetAnimations(document_);
else
GetAnimationsTargetingDocument(document_, animations);
std::sort(animations.begin(), animations.end(), CompareAnimations);
return animations;
}
void DocumentAnimations::Trace(blink::Visitor* visitor) {
visitor->Trace(document_);
visitor->Trace(timelines_);
}
void DocumentAnimations::GetAnimationsTargetingDocument(
Document* document_,
HeapVector<Member<Animation>>& animations) {
// This method follows the timelines in a given docmuent and append all the
// animations to the reference animations.
for (auto& timeline : timelines_) {
for (const auto& animation : timeline->GetAnimations()) {
if (animation->ReplaceStateRemoved())
......@@ -141,13 +164,5 @@ HeapVector<Member<Animation>> DocumentAnimations::getAnimations() {
animations.push_back(animation);
}
}
std::sort(animations.begin(), animations.end(), CompareAnimations);
return animations;
}
void DocumentAnimations::Trace(Visitor* visitor) {
visitor->Trace(document_);
visitor->Trace(timelines_);
}
} // namespace blink
......@@ -51,6 +51,8 @@ class CORE_EXPORT DocumentAnimations final
void UpdateAnimationTimingForAnimationFrame();
bool NeedsAnimationTimingUpdate();
void UpdateAnimationTimingIfNeeded();
void GetAnimationsTargetingDocument(Document*,
HeapVector<Member<Animation>>&);
// Updates existing animations as part of generating a new (document
// lifecycle) frame. Note that this considers and updates state for
......
......@@ -19,6 +19,22 @@
namespace blink {
namespace {
typedef HeapVector<Member<Document>, 32> DocumentsVector;
// We walk through all the frames in DOM tree order and get all the documents
DocumentsVector GetAllDocuments(Frame* main_frame) {
DocumentsVector documents;
for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) {
if (auto* local_frame = DynamicTo<LocalFrame>(frame))
documents.push_back(local_frame->GetDocument());
}
return documents;
}
} // namespace
PageAnimator::PageAnimator(Page& page)
: page_(page),
servicing_animations_(false),
......@@ -37,12 +53,7 @@ void PageAnimator::ServiceScriptedAnimations(
Clock().SetAllowedToDynamicallyUpdateTime(false);
Clock().UpdateTime(monotonic_animation_start_time);
HeapVector<Member<Document>, 32> documents;
for (Frame* frame = page_->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
if (auto* local_frame = DynamicTo<LocalFrame>(frame))
documents.push_back(local_frame->GetDocument());
}
DocumentsVector documents = GetAllDocuments(page_->MainFrame());
for (auto& document : documents) {
ScopedFrameBlamer frame_blamer(document->GetFrame());
......@@ -90,7 +101,7 @@ void PageAnimator::ServiceScriptedAnimations(
}
void PageAnimator::PostAnimate() {
HeapVector<Member<Document>, 32> documents;
DocumentsVector documents;
for (Frame* frame = page_->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
if (frame->IsLocalFrame())
......@@ -171,4 +182,14 @@ void PageAnimator::UpdateHitTestOcclusionData(LocalFrame& root_frame) {
}
}
HeapVector<Member<Animation>> PageAnimator::GetAnimations(Document* document_) {
HeapVector<Member<Animation>> animations;
DocumentsVector documents = GetAllDocuments(page_->MainFrame());
for (auto& document : documents) {
document->GetDocumentAnimations().GetAnimationsTargetingDocument(
document_, animations);
}
return animations;
}
} // namespace blink
......@@ -6,8 +6,11 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_PAGE_ANIMATOR_H_
#include "third_party/blink/public/common/metrics/document_update_reason.h"
#include "third_party/blink/renderer/core/animation/animation.h"
#include "third_party/blink/renderer/core/animation/animation_clock.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
......@@ -41,6 +44,7 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
void UpdateLifecycleToLayoutClean(LocalFrame& root_frame,
DocumentUpdateReason reason);
AnimationClock& Clock() { return animation_clock_; }
HeapVector<Member<Animation>> GetAnimations(Document*);
private:
void UpdateHitTestOcclusionData(LocalFrame& root_frame);
......
......@@ -4,7 +4,8 @@ PASS Document.getAnimations() returns script-generated animations
PASS Document.getAnimations() returns script-generated animations in the order they were created
PASS Document.getAnimations() does not return a disconnected node
PASS Document.getAnimations() does not return an animation with a null target
FAIL Document.getAnimations() returns animations on elements inside same-origin iframes assert_equals: expected 1 but got 0
PASS Document.getAnimations() returns animations on elements inside same-origin iframes
PASS iframe.contentDocument.getAnimations() returns animations on elements inside same-origin Document
FAIL ShadowRoot.getAnimations() return all animations in the shadow tree div.shadowRoot.getAnimations is not a function
FAIL Document.getAnimations() does NOT return animations in shadow trees assert_array_equals: getAnimations() called on Document does not return animations from shadow trees lengths differ, expected array [] length 0, got [object "[object Animation]"] length 1
PASS Document.getAnimations() triggers a style change event
......
......@@ -13,6 +13,16 @@
const gKeyFrames = { 'marginLeft': ['100px', '200px'] };
async function insert_frame_and_await_load(test, iframe, document) {
const eventWatcher = new EventWatcher(test, iframe, ['load']);
const event_promise = eventWatcher.wait_for('load');
document.body.appendChild(iframe);
test.add_cleanup(() => { document.body.removeChild(iframe); });
await event_promise;
}
test(t => {
assert_equals(document.getAnimations().length, 0,
'getAnimations returns an empty sequence for a document ' +
......@@ -68,14 +78,7 @@ test(t => {
promise_test(async t => {
const iframe = document.createElement('iframe');
const eventWatcher = new EventWatcher(t, iframe, ['load']);
const event_promise = eventWatcher.wait_for('load');
document.body.appendChild(iframe);
t.add_cleanup(() => { document.body.removeChild(iframe); });
await event_promise;
insert_frame_and_await_load(t, iframe, document)
const div = createDiv(t, iframe.contentDocument)
const effect = new KeyframeEffect(div, null, 100 * MS_PER_SEC);
......@@ -91,6 +94,52 @@ promise_test(async t => {
}, 'Document.getAnimations() returns animations on elements inside same-origin'
+ ' iframes');
promise_test(async t => {
const iframe1 = document.createElement('iframe');
const iframe2 = document.createElement('iframe');
insert_frame_and_await_load(t, iframe1, document);
insert_frame_and_await_load(t, iframe2, document);
const div_frame1 = createDiv(t, iframe1.contentDocument)
const div_main_frame = createDiv(t)
const effect1 = new KeyframeEffect(div_frame1, null, 100 * MS_PER_SEC);
const anim1 = new Animation(effect1, document.timeline);
anim1.play();
// Animation of div_frame1 is in iframe with main timeline.
// The animation's timeline is from the iframe, but the effect's target
// element is part of the iframe's document.
assert_equals(document.getAnimations().length, 0);
assert_equals(iframe1.contentDocument.getAnimations().length, 1);
anim1.finish();
// animation of div_frame1 in iframe1 with iframe timeline
const effect2 = new KeyframeEffect(div_frame1, null, 100 * MS_PER_SEC);
const anim2 = new Animation(effect2, iframe1.contentDocument.timeline);
anim2.play();
assert_equals(document.getAnimations().length, 0);
assert_equals(iframe1.contentDocument.getAnimations().length, 1);
anim2.finish();
//animation of div_main_frame in main frame with iframe timeline
const effect3 = new KeyframeEffect(div_main_frame, null, 100 * MS_PER_SEC);
const anim3 = new Animation(effect3, iframe1.contentDocument.timeline);
anim3.play();
assert_equals(document.getAnimations().length, 1);
assert_equals(iframe1.contentDocument.getAnimations().length, 0);
anim3.finish();
//animation of div_frame1 in iframe1 with another iframe's timeline
const effect4 = new KeyframeEffect(div_frame1, null, 100 * MS_PER_SEC);
const anim4 = new Animation(effect4, iframe2.contentDocument.timeline);
anim4.play();
assert_equals(document.getAnimations().length, 0);
assert_equals(iframe1.contentDocument.getAnimations().length, 1);
assert_equals(iframe2.contentDocument.getAnimations().length, 0);
anim4.finish();
}, 'iframe.contentDocument.getAnimations() returns animations on elements '
+ 'inside same-origin Document');
test(t => {
const div = createDiv(t);
const shadow = div.attachShadow({ mode: 'open' });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment