Bug 1590971 - Move getAnimations from Document to DocumentOrShadowRoot; r=emilio,baku

This updates the Gecko implementation to match the following change to
the Web Animations spec:

  792453b952 (diff-4c9f5c055fb219a7fcad23a9a7a80b64)

Differential Revision: https://phabricator.services.mozilla.com/D50768

--HG--
rename : testing/web-platform/tests/web-animations/interfaces/Document/getAnimations.html => testing/web-platform/tests/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
extra : moz-landing-system : lando
This commit is contained in:
Brian Birtles 2019-10-29 07:13:22 +00:00
Родитель fed2a8968b
Коммит e50dce95ba
9 изменённых файлов: 129 добавлений и 40 удалений

Просмотреть файл

@ -11,7 +11,6 @@
#include "AudioChannelService.h"
#include "mozilla/dom/Document.h"
#include "DocumentInlines.h"
#include "mozilla/AnimationComparator.h"
#include "mozilla/AntiTrackingCommon.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/AutoRestore.h"
@ -3857,18 +3856,6 @@ DocumentTimeline* Document::Timeline() {
return mDocumentTimeline;
}
void Document::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations) {
// Hold a strong ref for the root element since Element::GetAnimations() calls
// FlushPendingNotifications() which may destroy the element.
RefPtr<Element> root = GetRootElement();
if (!root) {
return;
}
GetAnimationsOptions options;
options.mSubtree = true;
root->GetAnimations(options, aAnimations);
}
SVGSVGElement* Document::GetSVGRootElement() const {
Element* root = GetRootElement();
if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {

Просмотреть файл

@ -165,7 +165,6 @@ class Rule;
} // namespace css
namespace dom {
class Animation;
class AnonymousContent;
class Attr;
class XULBroadcastManager;
@ -3291,8 +3290,6 @@ class Document : public nsINode,
DocumentTimeline* Timeline();
LinkedList<DocumentTimeline>& Timelines() { return mTimelines; }
void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
SVGSVGElement* GetSVGRootElement() const;
struct FrameRequest {

Просмотреть файл

@ -5,8 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocumentOrShadowRoot.h"
#include "mozilla/AnimationComparator.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/AnimatableBinding.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/ShadowRoot.h"
@ -451,6 +453,31 @@ struct nsRadioGroupStruct {
bool mGroupSuffersFromValueMissing;
};
void DocumentOrShadowRoot::GetAnimations(
nsTArray<RefPtr<Animation>>& aAnimations) {
// As with Element::GetAnimations we initially flush style here.
// This should ensure that there are no subsequent changes to the tree
// structure while iterating over the children below.
if (Document* doc = AsNode().GetComposedDoc()) {
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
GetAnimationsOptions options;
options.mSubtree = true;
for (RefPtr<nsIContent> child = AsNode().GetFirstChild(); child;
child = child->GetNextSibling()) {
if (RefPtr<Element> element = Element::FromNode(child)) {
nsTArray<RefPtr<Animation>> result;
element->GetAnimations(options, result, Element::Flush::No);
aAnimations.AppendElements(std::move(result));
}
}
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}
nsresult DocumentOrShadowRoot::WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent) {

Просмотреть файл

@ -25,6 +25,7 @@ class StyleSheet;
namespace dom {
class Animation;
class Element;
class DocumentOrShadowRoot;
class HTMLInputElement;
@ -178,6 +179,10 @@ class DocumentOrShadowRoot {
void ReportEmptyGetElementByIdArg();
// Web Animations
MOZ_CAN_RUN_SCRIPT
void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
// nsIRadioGroupContainer
NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor,
bool aFlushContent);

Просмотреть файл

@ -3425,15 +3425,21 @@ already_AddRefed<Animation> Element::Animate(
}
void Element::GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations) {
Document* doc = GetComposedDoc();
if (doc) {
// We don't need to explicitly flush throttled animations here, since
// updating the animation style of elements will never affect the set of
// running animations and it's only the set of running animations that is
// important here.
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
nsTArray<RefPtr<Animation>>& aAnimations,
Flush aFlush) {
if (aFlush == Flush::Yes) {
if (Document* doc = GetComposedDoc()) {
// We don't need to explicitly flush throttled animations here, since
// updating the animation style of elements will never affect the set of
// running animations and it's only the set of running animations that is
// important here.
//
// NOTE: Any changes to the flags passed to the following call should
// be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
// too.
doc->FlushPendingNotifications(
ChangesToFlush(FlushType::Style, false /* flush animations */));
}
}
Element* elem = this;

Просмотреть файл

@ -1336,11 +1336,13 @@ class Element : public FragmentOrElement {
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
// Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
// Callers must keep this element alive because flushing style may destroy
// this element.
enum class Flush { Yes, No };
MOZ_CAN_RUN_SCRIPT
void GetAnimations(const GetAnimationsOptions& aOptions,
nsTArray<RefPtr<Animation>>& aAnimations);
nsTArray<RefPtr<Animation>>& aAnimations,
Flush aFlush = Flush::Yes);
static void GetAnimationsUnsorted(Element* aElement,
PseudoStyleType aPseudoType,
nsTArray<RefPtr<Animation>>& aAnimations);

Просмотреть файл

@ -367,8 +367,6 @@ partial interface Document {
partial interface Document {
[Func="Document::AreWebAnimationsTimelinesEnabled"]
readonly attribute DocumentTimeline timeline;
[Func="Document::IsWebAnimationsGetAnimationsEnabled"]
sequence<Animation> getAnimations();
};
// https://svgwg.org/svg2-draft/struct.html#InterfaceDocumentExtensions

Просмотреть файл

@ -33,3 +33,9 @@ interface mixin DocumentOrShadowRoot {
[BinaryName="fullscreenElement"]
readonly attribute Element? mozFullScreenElement;
};
// https://drafts.csswg.org/web-animations-1/#extensions-to-the-documentorshadowroot-interface-mixin
partial interface mixin DocumentOrShadowRoot {
[Func="Document::IsWebAnimationsGetAnimationsEnabled"]
sequence<Animation> getAnimations();
};

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Document.getAnimations</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-document-getanimations">
<title>DocumentOrShadowRoot.getAnimations</title>
<link rel="help" href="https://drafts.csswg.org/web-animations-1/#dom-documentorshadowroot-getanimations">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
@ -17,7 +17,8 @@ test(t => {
assert_equals(document.getAnimations().length, 0,
'getAnimations returns an empty sequence for a document ' +
'with no animations');
}, 'Test document.getAnimations for non-animated content');
}, 'Document.getAnimations() returns an empty sequence for non-animated'
+ ' content');
test(t => {
const div = createDiv(t);
@ -30,7 +31,7 @@ test(t => {
anim2.finish();
assert_equals(document.getAnimations().length, 0,
'getAnimation only returns running animations');
}, 'Test document.getAnimations for script-generated animations')
}, 'Document.getAnimations() returns script-generated animations')
test(t => {
const div = createDiv(t);
@ -39,7 +40,8 @@ test(t => {
assert_array_equals(document.getAnimations(),
[ anim1, anim2 ],
'getAnimations() returns running animations');
}, 'Test the order of document.getAnimations with script generated animations')
}, 'Document.getAnimations() returns script-generated animations in the order'
+ ' they were created')
test(t => {
// This element exists but is not a descendent of any document, so isn't
@ -52,7 +54,7 @@ test(t => {
document.body.appendChild(div);
t.add_cleanup(() => { div.remove(); });
assert_equals(document.getAnimations().length, 1);
}, 'Test document.getAnimations for a disconnected node');
}, 'Document.getAnimations() does not return a disconnected node');
test(t => {
const effect = new KeyframeEffect(null, gKeyFrames, 100 * MS_PER_SEC);
@ -62,7 +64,7 @@ test(t => {
assert_equals(document.getAnimations().length, 0,
'document.getAnimations() only returns animations targeting ' +
'elements in this document');
}, 'Test document.getAnimations with null target');
}, 'Document.getAnimations() does not return an animation with a null target');
promise_test(async t => {
const iframe = document.createElement('iframe');
@ -86,7 +88,66 @@ promise_test(async t => {
assert_equals(document.getAnimations().length, 0);
assert_equals(iframe.contentDocument.getAnimations().length, 1);
anim.finish();
}, 'Test document.getAnimations for elements inside same-origin iframes');
}, 'Document.getAnimations() returns animations on elements inside same-origin'
+ ' iframes');
test(t => {
const div = createDiv(t);
const shadow = div.attachShadow({ mode: 'open' });
// Create a tree with the following structure
//
// div
// |
// (ShadowRoot)
// / \
// childA childB
// (*anim2) |
// grandChild
// (*anim1)
//
// This lets us test that:
//
// a) All children of the ShadowRoot are included
// b) Descendants of the children are included
// c) The result is sorted by composite order (since we fire anim1 before
// anim2 despite childA appearing first in tree order)
const childA = createDiv(t);
shadow.append(childA);
const childB = createDiv(t);
shadow.append(childB);
const grandChild = createDiv(t);
childB.append(grandChild);
const anim1 = grandChild.animate(gKeyFrames, 100 * MS_PER_SEC)
const anim2 = childA.animate(gKeyFrames, 100 * MS_PER_SEC)
assert_array_equals(
div.shadowRoot.getAnimations(),
[ anim1, anim2 ],
'getAnimations() called on ShadowRoot returns expected animations'
);
}, 'ShadowRoot.getAnimations() return all animations in the shadow tree');
test(t => {
const div = createDiv(t);
const shadow = div.attachShadow({ mode: 'open' });
const child = createDiv(t);
shadow.append(child);
child.animate(gKeyFrames, 100 * MS_PER_SEC)
assert_array_equals(
document.getAnimations(),
[],
'getAnimations() called on Document does not return animations from shadow'
+ ' trees'
);
}, 'Document.getAnimations() does NOT return animations in shadow trees');
promise_test(async t => {
const div = createDiv(t);
@ -114,7 +175,7 @@ promise_test(async t => {
// If getAnimations() flushed style, we should get a transitionrun event.
await watcher.wait_for('transitionrun');
}, 'Triggers a style change event');
}, 'Document.getAnimations() triggers a style change event');
</script>
</body>