зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
fed2a8968b
Коммит
e50dce95ba
|
@ -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>
|
Загрузка…
Ссылка в новой задаче