diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp
index ea88c65f7a..f2779bebb0 100644
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -91,7 +91,12 @@ private:
virtual void run(const MatchFinder::MatchResult &Result);
};
- class NonMemMovableChecker : public MatchFinder::MatchCallback {
+ class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NonMemMovableMemberChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
};
@@ -125,7 +130,8 @@ private:
ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
- NonMemMovableChecker nonMemMovableChecker;
+ NonMemMovableTemplateArgChecker nonMemMovableTemplateArgChecker;
+ NonMemMovableMemberChecker nonMemMovableMemberChecker;
ExplicitImplicitChecker explicitImplicitChecker;
NoAutoTypeChecker noAutoTypeChecker;
NoExplicitMoveConstructorChecker noExplicitMoveConstructorChecker;
@@ -201,7 +207,8 @@ bool isIgnoredPathForImplicitCtor(const Decl *decl) {
begin->compare_lower(StringRef("harfbuzz")) == 0 ||
begin->compare_lower(StringRef("hunspell")) == 0 ||
begin->compare_lower(StringRef("scoped_ptr.h")) == 0 ||
- begin->compare_lower(StringRef("graphite2")) == 0) {
+ begin->compare_lower(StringRef("graphite2")) == 0 ||
+ begin->compare_lower(StringRef("icu")) == 0) {
return true;
}
if (begin->compare_lower(StringRef("chromium")) == 0) {
@@ -710,10 +717,15 @@ AST_MATCHER(QualType, isNonMemMovable) {
}
/// This matcher will select classes which require a memmovable template arg
-AST_MATCHER(CXXRecordDecl, needsMemMovable) {
+AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
}
+/// This matcher will select classes which require all members to be memmovable
+AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
+}
+
AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
const CXXConstructorDecl *decl = Node.getCanonicalDecl();
return
@@ -1015,10 +1027,16 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
// Handle non-mem-movable template specializations
astMatcher.addMatcher(
classTemplateSpecializationDecl(
- allOf(needsMemMovable(),
+ allOf(needsMemMovableTemplateArg(),
hasAnyTemplateArgument(refersToType(isNonMemMovable()))))
.bind("specialization"),
- &nonMemMovableChecker);
+ &nonMemMovableTemplateArgChecker);
+
+ // Handle non-mem-movable members
+ astMatcher.addMatcher(
+ cxxRecordDecl(needsMemMovableMembers())
+ .bind("decl"),
+ &nonMemMovableMemberChecker);
astMatcher.addMatcher(constructorDecl(isInterestingImplicitCtor(),
ofClass(allOf(isConcreteClass(),
@@ -1385,7 +1403,7 @@ void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
<< specialization;
}
-void DiagnosticsMatcher::NonMemMovableChecker::run(
+void DiagnosticsMatcher::NonMemMovableTemplateArgChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
@@ -1406,7 +1424,7 @@ void DiagnosticsMatcher::NonMemMovableChecker::run(
specialization->getTemplateInstantiationArgs();
for (unsigned i = 0; i < args.size(); ++i) {
QualType argType = args[i].getAsType();
- if (NonMemMovable.hasEffectiveAnnotation(args[i].getAsType())) {
+ if (NonMemMovable.hasEffectiveAnnotation(argType)) {
Diag.Report(specialization->getLocation(), errorID) << specialization
<< argType;
// XXX It would be really nice if we could get the instantiation stack
@@ -1424,6 +1442,29 @@ void DiagnosticsMatcher::NonMemMovableChecker::run(
}
}
+void DiagnosticsMatcher::NonMemMovableMemberChecker::run(
+ const MatchFinder::MatchResult &Result) {
+ DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+ unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Error,
+ "class %0 cannot have non-memmovable member %1 of type %2");
+
+ // Get the specialization
+ const CXXRecordDecl* Decl =
+ Result.Nodes.getNodeAs("decl");
+
+ // Report an error for every member which is non-memmovable
+ for (const FieldDecl *Field : Decl->fields()) {
+ QualType Type = Field->getType();
+ if (NonMemMovable.hasEffectiveAnnotation(Type)) {
+ Diag.Report(Field->getLocation(), errorID) << Decl
+ << Field
+ << Type;
+ NonMemMovable.dumpAnnotationReason(Diag, Type, Decl->getLocation());
+ }
+ }
+}
+
void DiagnosticsMatcher::ExplicitImplicitChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
diff --git a/build/clang-plugin/tests/TestNonMemMovable.cpp b/build/clang-plugin/tests/TestNonMemMovable.cpp
index 3665fadaea..a726d0ab6b 100644
--- a/build/clang-plugin/tests/TestNonMemMovable.cpp
+++ b/build/clang-plugin/tests/TestNonMemMovable.cpp
@@ -1,5 +1,6 @@
#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
+#define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members")))
/*
These are a bunch of structs with variable levels of memmovability.
@@ -9,12 +10,12 @@ struct MOZ_NON_MEMMOVABLE NonMovable {};
struct Movable {};
// Subclasses
-struct S_NonMovable : NonMovable {}; // expected-note 48 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}}
+struct S_NonMovable : NonMovable {}; // expected-note 51 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}}
struct S_Movable : Movable {};
// Members
struct W_NonMovable {
- NonMovable m; // expected-note 32 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}}
+ NonMovable m; // expected-note 34 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}}
};
struct W_Movable {
Movable m;
@@ -22,17 +23,17 @@ struct W_Movable {
// Wrapped Subclasses
struct WS_NonMovable {
- S_NonMovable m; // expected-note 32 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}}
+ S_NonMovable m; // expected-note 34 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}}
};
struct WS_Movable {
S_Movable m;
};
// Combinations of the above
-struct SW_NonMovable : W_NonMovable {}; // expected-note 16 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}}
+struct SW_NonMovable : W_NonMovable {}; // expected-note 17 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}}
struct SW_Movable : W_Movable {};
-struct SWS_NonMovable : WS_NonMovable {}; // expected-note 16 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}}
+struct SWS_NonMovable : WS_NonMovable {}; // expected-note 17 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}}
struct SWS_Movable : WS_Movable {};
// Basic templated wrapper
@@ -810,3 +811,20 @@ void specialization() {
Defaulted_Templated_NeedyTemplate7 c7;
W_Defaulted_Templated_NeedyTemplate8 c8;
}
+
+class MOZ_NEEDS_MEMMOVABLE_MEMBERS NeedsMemMovableMembers {
+ Movable m1;
+ NonMovable m2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'm2' of type 'NonMovable'}}
+ S_Movable sm1;
+ S_NonMovable sm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'sm2' of type 'S_NonMovable'}}
+ W_Movable wm1;
+ W_NonMovable wm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wm2' of type 'W_NonMovable'}}
+ SW_Movable swm1;
+ SW_NonMovable swm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swm2' of type 'SW_NonMovable'}}
+ WS_Movable wsm1;
+ WS_NonMovable wsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wsm2' of type 'WS_NonMovable'}}
+ SWS_Movable swsm1;
+ SWS_NonMovable swsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swsm2' of type 'SWS_NonMovable'}}
+};
+
+class NeedsMemMovableMembersDerived : public NeedsMemMovableMembers {};
diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp
index 8545a13df7..57fbd8f0ba 100644
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -454,6 +454,13 @@ BasePrincipal::GetAppId(uint32_t* aAppId)
return NS_OK;
}
+NS_IMETHODIMP
+BasePrincipal::GetAddonId(nsAString& aAddonId)
+{
+ aAddonId.Assign(mOriginAttributes.mAddonId);
+ return NS_OK;
+}
+
NS_IMETHODIMP
BasePrincipal::GetUserContextId(uint32_t* aUserContextId)
{
@@ -507,6 +514,28 @@ BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAt
return codebase.forget();
}
+already_AddRefed
+BasePrincipal::CreateCodebasePrincipal(const nsACString& aOrigin)
+{
+ MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("[")),
+ "CreateCodebasePrincipal does not support System and Expanded principals");
+
+ MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":")),
+ "CreateCodebasePrincipal does not support nsNullPrincipal");
+
+ nsAutoCString originNoSuffix;
+ mozilla::OriginAttributes attrs;
+ if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
+ return nullptr;
+ }
+
+ nsCOMPtr uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+}
+
bool
BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
{
diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h
index d8faed7f82..f47720c228 100644
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -157,6 +157,7 @@ public:
NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
+ NS_IMETHOD GetAddonId(nsAString& aAddonId) final;
NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) final;
NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
@@ -168,6 +169,7 @@ public:
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast(aPrin); }
static already_AddRefed
CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs);
+ static already_AddRefed CreateCodebasePrincipal(const nsACString& aOrigin);
const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
uint32_t AppId() const { return mOriginAttributes.mAppId; }
diff --git a/caps/nsIAddonPolicyService.idl b/caps/nsIAddonPolicyService.idl
index 7a9c1f6621..ff0db01f41 100644
--- a/caps/nsIAddonPolicyService.idl
+++ b/caps/nsIAddonPolicyService.idl
@@ -14,6 +14,25 @@
[scriptable,uuid(8a034ef9-9d14-4c5d-8319-06c1ab574baa)]
interface nsIAddonPolicyService : nsISupports
{
+ /**
+ * Returns the base content security policy, which is applied to all
+ * extension documents, in addition to any custom policies.
+ */
+ readonly attribute AString baseCSP;
+
+ /**
+ * Returns the default content security policy which applies to extension
+ * documents which do not specify any custom policies.
+ */
+ readonly attribute AString defaultCSP;
+
+ /**
+ * Returns the content security policy which applies to documents belonging
+ * to the extension with the given ID. This may be either a custom policy,
+ * if one was supplied, or the default policy if one was not.
+ */
+ AString getAddonCSP(in AString aAddonId);
+
/**
* Returns true if unprivileged code associated with the given addon may load
* data from |aURI|.
@@ -30,3 +49,18 @@ interface nsIAddonPolicyService : nsISupports
*/
AString extensionURIToAddonId(in nsIURI aURI);
};
+
+/**
+ * This interface exposes functionality related to add-on content policy
+ * enforcement.
+ */
+[scriptable,uuid(7a4fe60b-9131-45f5-83f3-dc63b5d71a5d)]
+interface nsIAddonContentPolicy : nsISupports
+{
+ /**
+ * Checks a custom content security policy string, to ensure that it meets
+ * minimum security requirements. Returns null for valid policies, or a
+ * string describing the error for invalid policies.
+ */
+ AString validateAddonCSP(in AString aPolicyString);
+};
diff --git a/caps/nsIPrincipal.idl b/caps/nsIPrincipal.idl
index 277561a838..eb7c109de2 100644
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -286,6 +286,11 @@ interface nsIPrincipal : nsISerializable
*/
[infallible] readonly attribute unsigned long appId;
+ /**
+ * Gets the ID of the add-on this principal belongs to.
+ */
+ readonly attribute AString addonId;
+
/**
* Gets the id of the user context this principal is inside. If this
* principal is inside the default userContext, this returns
diff --git a/caps/nsIScriptSecurityManager.idl b/caps/nsIScriptSecurityManager.idl
index 6147f5feb1..dced0afcd7 100644
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -26,7 +26,7 @@ class DomainPolicyClone;
[ptr] native JSObjectPtr(JSObject);
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
-[scriptable, uuid(73f92674-f59d-4c9b-a9b5-f7a3ae8ffa98)]
+[scriptable, uuid(b7ae2310-576e-11e5-a837-0800200c9a66)]
interface nsIScriptSecurityManager : nsISupports
{
/**
@@ -195,6 +195,13 @@ interface nsIScriptSecurityManager : nsISupports
[implicit_jscontext]
nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes);
+ /**
+ * Returns a principal whose origin is the one we pass in.
+ * See nsIPrincipal.idl for a description of origin attributes, and
+ * ChromeUtils.webidl for a list of origin attributes and their defaults.
+ */
+ nsIPrincipal createCodebasePrincipalFromOrigin(in ACString origin);
+
/**
* Returns a unique nonce principal with |originAttributes|.
* See nsIPrincipal.h for a description of origin attributes, and
diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp
index 740cd63788..4b0152f1be 100644
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -68,6 +68,7 @@
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "nsILoadInfo.h"
+#include "nsXPCOMStrings.h"
// This should be probably defined on some other place... but I couldn't find it
#define WEBAPPS_PERM_NAME "webapps-manage"
@@ -1096,6 +1097,23 @@ nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, JS::Handle prin = BasePrincipal::CreateCodebasePrincipal(aOrigin);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
NS_IMETHODIMP
nsScriptSecurityManager::CreateNullPrincipal(JS::Handle aOriginAttributes,
JSContext* aCx, nsIPrincipal** aPrincipal)
diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js
index b492ef97d0..dc2b8975bd 100644
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -3,7 +3,6 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/BrowserUtils.jsm");
var ssm = Services.scriptSecurityManager;
function makeURI(uri) { return Services.io.newURI(uri, null, null); }
@@ -28,9 +27,9 @@ function checkOriginAttributes(prin, attrs, suffix) {
do_check_eq(prin.originAttributes.inBrowser, attrs.inBrowser || false);
do_check_eq(prin.originSuffix, suffix || '');
if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
- do_check_true(BrowserUtils.principalFromOrigin(prin.origin).equals(prin));
+ do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin));
} else {
- checkThrows(() => BrowserUtils.principalFromOrigin(prin.origin));
+ checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin));
}
}
diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp
index b46189f770..b120f35ce0 100644
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1230,12 +1230,14 @@ Animation::GetPresContext() const
void
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
{
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
DoFinishNotificationImmediately();
} else if (!mFinishNotificationTask.IsPending()) {
RefPtr> runnable =
NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
- Promise::DispatchToMicroTask(runnable);
+ runtime->DispatchToMicroTask(runnable);
mFinishNotificationTask = runnable;
}
}
diff --git a/dom/animation/CSSPseudoElement.cpp b/dom/animation/CSSPseudoElement.cpp
index 588d13aaaa..8712e07c03 100644
--- a/dom/animation/CSSPseudoElement.cpp
+++ b/dom/animation/CSSPseudoElement.cpp
@@ -7,6 +7,7 @@
#include "mozilla/dom/CSSPseudoElement.h"
#include "mozilla/dom/CSSPseudoElementBinding.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/AnimationComparator.h"
namespace mozilla {
namespace dom {
@@ -49,10 +50,16 @@ CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle aGivenProto)
}
void
-CSSPseudoElement::GetAnimations(nsTArray>& aRetVal)
+CSSPseudoElement::GetAnimations(const AnimationFilter& filter,
+ nsTArray>& aRetVal)
{
- // Bug 1234403: Implement this API.
- NS_NOTREACHED("CSSPseudoElement::GetAnimations() is not implemented yet.");
+ nsIDocument* doc = mParentElement->GetComposedDoc();
+ if (doc) {
+ doc->FlushPendingNotifications(Flush_Style);
+ }
+
+ Element::GetAnimationsUnsorted(mParentElement, mPseudoType, aRetVal);
+ aRetVal.Sort(AnimationPtrComparator>());
}
already_AddRefed
diff --git a/dom/animation/CSSPseudoElement.h b/dom/animation/CSSPseudoElement.h
index 4082a07aa7..8631eba011 100644
--- a/dom/animation/CSSPseudoElement.h
+++ b/dom/animation/CSSPseudoElement.h
@@ -57,7 +57,8 @@ public:
return retVal.forget();
}
- void GetAnimations(nsTArray>& aRetVal);
+ void GetAnimations(const AnimationFilter& filter,
+ nsTArray>& aRetVal);
already_AddRefed
Animate(JSContext* aContext,
JS::Handle aFrames,
diff --git a/dom/animation/TimingParams.cpp b/dom/animation/TimingParams.cpp
index 79603ca486..5f64241d66 100644
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -161,7 +161,7 @@ TimingParams::ParseEasing(const nsAString& aEasing,
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
break;
}
- aRv.ThrowTypeError();
+ aRv.ThrowTypeError(aEasing);
return Nothing();
}
diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h
index a97322569c..9ea596a072 100644
--- a/dom/animation/TimingParams.h
+++ b/dom/animation/TimingParams.h
@@ -60,7 +60,8 @@ struct TimingParams
NS_LITERAL_STRING("duration"));
}
} else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
- aRv.ThrowTypeError();
+ aRv.ThrowTypeError(
+ aDuration.GetAsString());
}
return result;
}
diff --git a/dom/animation/test/chrome.ini b/dom/animation/test/chrome.ini
index c5fae26349..761e266c3d 100644
--- a/dom/animation/test/chrome.ini
+++ b/dom/animation/test/chrome.ini
@@ -9,8 +9,9 @@ support-files =
# file_animate_xrays.html needs to go in mochitest.ini since it is served
# over HTTP
[chrome/test_animation_observers.html]
-[chrome/test_animation_properties.html]
[chrome/test_animation_property_state.html]
+[chrome/test_animation_properties.html]
+[chrome/test_generated_content_getAnimations.html]
[chrome/test_restyles.html]
[chrome/test_running_on_compositor.html]
skip-if = buildapp == 'b2g'
diff --git a/dom/animation/test/chrome/test_generated_content_getAnimations.html b/dom/animation/test/chrome/test_generated_content_getAnimations.html
new file mode 100644
index 0000000000..04e0f9bd09
--- /dev/null
+++ b/dom/animation/test/chrome/test_generated_content_getAnimations.html
@@ -0,0 +1,83 @@
+
+
+
+Test getAnimations() for generated-content elements
+
+
+
+
+
+
+
+
+
diff --git a/dom/animation/test/css-animations/file_document-get-animations.html b/dom/animation/test/css-animations/file_document-get-animations.html
index f57c3cb5b2..abe02d7fc3 100644
--- a/dom/animation/test/css-animations/file_document-get-animations.html
+++ b/dom/animation/test/css-animations/file_document-get-animations.html
@@ -14,10 +14,6 @@
@keyframes animRight {
to { right: 100px }
}
-.with-before-animation::before {
- content: " ";
- animation: animLeft 100s;
-}
diff --git a/dom/animation/test/css-animations/file_effect-target.html b/dom/animation/test/css-animations/file_effect-target.html
index 022715634c..9ca78a887a 100644
--- a/dom/animation/test/css-animations/file_effect-target.html
+++ b/dom/animation/test/css-animations/file_effect-target.html
@@ -14,7 +14,40 @@ test(function(t) {
var animation = div.getAnimations()[0];
assert_equals(animation.effect.target, div,
'Animation.target is the animatable div');
-}, 'Returned CSS animations have the correct Animation.target');
+}, 'Returned CSS animations have the correct effect target');
+
+test(function(t) {
+ addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
+ var div = addDiv(t, { class: 'after' });
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_equals(anims[0].effect.target, anims[1].effect.target,
+ 'Both animations return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+ addStyle(t, { '.after::after': 'animation: anim 10s;' });
+ var div = addDiv(t, { class: 'after' });
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnim = new Animation(effect, document.timeline);
+ newAnim.play();
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_not_equals(anims[0], newAnim,
+ 'The scriped-generated animation appears last');
+ assert_equals(newAnim.effect.target, pseudoTarget,
+ 'The effect.target of the scripted-generated animation is ' +
+ 'the same as the one from the argument of ' +
+ 'KeyframeEffectReadOnly constructor');
+ assert_equals(anims[0].effect.target, newAnim.effect.target,
+ 'Both animations return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+ 'CSSPseudoElement object as that from the CSS generated animation');
done();
diff --git a/dom/animation/test/css-animations/file_element-get-animations.html b/dom/animation/test/css-animations/file_element-get-animations.html
index df0476c025..c5f132f235 100644
--- a/dom/animation/test/css-animations/file_element-get-animations.html
+++ b/dom/animation/test/css-animations/file_element-get-animations.html
@@ -304,6 +304,147 @@ async_test(function(t) {
}));
}, 'getAnimations for CSS Animations follows animation-name order');
+test(function(t) {
+ addStyle(t, { '#target::after': 'animation: anim1 10s;',
+ '#target::before': 'animation: anim1 10s;' });
+ var target = addDiv(t, { 'id': 'target' });
+ target.style.animation = 'anim1 100s';
+
+ var animations = target.getAnimations({ subtree: false });
+ assert_equals(animations.length, 1,
+ 'Should find only the element');
+ assert_equals(animations[0].effect.target, target,
+ 'Effect target should be the element');
+}, 'Test AnimationFilter{ subtree: false } with single element');
+
+test(function(t) {
+ addStyle(t, { '#target::after': 'animation: anim1 10s;',
+ '#target::before': 'animation: anim1 10s;' });
+ var target = addDiv(t, { 'id': 'target' });
+ target.style.animation = 'anim1 100s';
+
+ var animations = target.getAnimations({ subtree: true });
+ assert_equals(animations.length, 3,
+ 'getAnimations({ subtree: true }) ' +
+ 'should return animations on pseudo-elements');
+ assert_equals(animations[0].effect.target, target,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+ assert_equals(animations[1].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned second');
+ assert_equals(animations[2].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned last');
+}, 'Test AnimationFilter{ subtree: true } with single element');
+
+test(function(t) {
+ addStyle(t, { '#parent::after': 'animation: anim1 10s;',
+ '#parent::before': 'animation: anim1 10s;',
+ '#child::after': 'animation: anim1 10s;',
+ '#child::before': 'animation: anim1 10s;' });
+ var parent = addDiv(t, { 'id': 'parent' });
+ parent.style.animation = 'anim1 100s';
+ var child = addDiv(t, { 'id': 'child' });
+ child.style.animation = 'anim1 100s';
+ parent.appendChild(child);
+
+ var animations = parent.getAnimations({ subtree: false });
+ assert_equals(animations.length, 1,
+ 'Should find only the element even if it has a child');
+ assert_equals(animations[0].effect.target, parent,
+ 'Effect target shuld be the element');
+}, 'Test AnimationFilter{ subtree: false } with element that has a child');
+
+test(function(t) {
+ addStyle(t, { '#parent::after': 'animation: anim1 10s;',
+ '#parent::before': 'animation: anim1 10s;',
+ '#child::after': 'animation: anim1 10s;',
+ '#child::before': 'animation: anim1 10s;' });
+ var parent = addDiv(t, { 'id': 'parent' });
+ var child = addDiv(t, { 'id': 'child' });
+ parent.style.animation = 'anim1 100s';
+ child.style.animation = 'anim1 100s';
+ parent.appendChild(child);
+
+ var animations = parent.getAnimations({ subtree: true });
+ assert_equals(animations.length, 6,
+ 'Should find all elements, pesudo-elements that parent has');
+
+ assert_equals(animations[0].effect.target, parent,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+ assert_equals(animations[1].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned second');
+ assert_equals(animations[1].effect.target.parentElement, parent,
+ 'This ::before element should be child of parent element');
+ assert_equals(animations[2].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned third');
+ assert_equals(animations[2].effect.target.parentElement, parent,
+ 'This ::after element should be child of parent element');
+
+ assert_equals(animations[3].effect.target, child,
+ 'The animation targeting the child element ' +
+ 'should be returned fourth');
+ assert_equals(animations[4].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned fifth');
+ assert_equals(animations[4].effect.target.parentElement, child,
+ 'This ::before element should be child of child element');
+ assert_equals(animations[5].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned last');
+ assert_equals(animations[5].effect.target.parentElement, child,
+ 'This ::after element should be child of child element');
+}, 'Test AnimationFilter{ subtree: true } with element that has a child');
+
+test(function(t) {
+ var parent = addDiv(t, { 'id': 'parent' });
+ var child1 = addDiv(t, { 'id': 'child1' });
+ var grandchild1 = addDiv(t, { 'id': 'grandchild1' });
+ var grandchild2 = addDiv(t, { 'id': 'grandchild2' });
+ var child2 = addDiv(t, { 'id': 'child2' });
+
+ parent.style.animation = 'anim1 100s';
+ child1.style.animation = 'anim1 100s';
+ grandchild1.style.animation = 'anim1 100s';
+ grandchild2.style.animation = 'anim1 100s';
+ child2.style.animation = 'anim1 100s';
+
+ parent.appendChild(child1);
+ child1.appendChild(grandchild1);
+ child1.appendChild(grandchild2);
+ parent.appendChild(child2);
+
+ var animations = parent.getAnimations({ subtree: true });
+ assert_equals(
+ parent.getAnimations({ subtree: true }).length, 5,
+ 'Should find all descendants of the element');
+
+ assert_equals(animations[0].effect.target, parent,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+
+ assert_equals(animations[1].effect.target, child1,
+ 'The animation targeting the child1 element ' +
+ 'should be returned second');
+
+ assert_equals(animations[2].effect.target, grandchild1,
+ 'The animation targeting the grandchild1 element ' +
+ 'should be returned third');
+
+ assert_equals(animations[3].effect.target, grandchild2,
+ 'The animation targeting the grandchild2 element ' +
+ 'should be returned fourth');
+
+ assert_equals(animations[4].effect.target, child2,
+ 'The animation targeting the child2 element ' +
+ 'should be returned last');
+
+}, 'Test AnimationFilter{ subtree: true } with element that has many descendant');
+
done();
diff --git a/dom/animation/test/css-animations/file_pseudoElement-get-animations.html b/dom/animation/test/css-animations/file_pseudoElement-get-animations.html
new file mode 100644
index 0000000000..de1abfbb1c
--- /dev/null
+++ b/dom/animation/test/css-animations/file_pseudoElement-get-animations.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
diff --git a/dom/animation/test/css-animations/test_pseudoElement-get-animations.html b/dom/animation/test/css-animations/test_pseudoElement-get-animations.html
new file mode 100644
index 0000000000..1e0dc5c825
--- /dev/null
+++ b/dom/animation/test/css-animations/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/dom/animation/test/css-transitions/file_document-get-animations.html b/dom/animation/test/css-transitions/file_document-get-animations.html
index d49b2dd5d5..a5d55b76cd 100644
--- a/dom/animation/test/css-transitions/file_document-get-animations.html
+++ b/dom/animation/test/css-transitions/file_document-get-animations.html
@@ -31,6 +31,49 @@ test(function(t) {
'getAnimations returns no running CSS Transitions');
}, 'getAnimations for CSS Transitions');
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
+ 'transition: all 100s;',
+ '.init::before': 'content: ""; width: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px;',
+ '.change::before': 'width: 100px;' });
+ // create two divs with these arrangement:
+ // parent
+ // ::before,
+ // ::after
+ // |
+ // child
+ var parent = addDiv(t);
+ var child = addDiv(t);
+ parent.appendChild(child);
+
+ parent.style.left = '0px';
+ parent.style.transition = 'left 10s';
+ parent.classList.add('init');
+ child.style.left = '0px';
+ child.style.transition = 'left 10s';
+ flushComputedStyle(parent);
+
+ parent.style.left = '100px';
+ parent.classList.add('change');
+ child.style.left = '100px';
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 4,
+ 'CSS transition on both pseudo-elements and elements ' +
+ 'are returned');
+ assert_equals(anims[0].effect.target, parent,
+ 'The animation targeting the parent element comes first');
+ assert_equals(anims[1].effect.target.type, '::before',
+ 'The animation targeting the ::before element comes second');
+ assert_equals(anims[2].effect.target.type, '::after',
+ 'The animation targeting the ::after element comes third');
+ assert_equals(anims[3].effect.target, child,
+ 'The animation targeting the child element comes last');
+}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
+ 'after sorting');
+
async_test(function(t) {
var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
flushComputedStyle(div);
diff --git a/dom/animation/test/css-transitions/file_effect-target.html b/dom/animation/test/css-transitions/file_effect-target.html
index b54bff8d39..0f67b0b9aa 100644
--- a/dom/animation/test/css-transitions/file_effect-target.html
+++ b/dom/animation/test/css-transitions/file_effect-target.html
@@ -18,6 +18,49 @@ test(function(t) {
'Animation.target is the animatable div');
}, 'Returned CSS transitions have the correct Animation.target');
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px; height: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got transitions running on ::after pseudo element');
+ assert_equals(anims[0].effect.target, anims[1].effect.target,
+ 'Both transitions return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
+ '.change::after': 'width: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnim = new Animation(effect, document.timeline);
+ newAnim.play();
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_not_equals(anims[0], newAnim,
+ 'The scriped-generated animation appears last');
+ assert_equals(newAnim.effect.target, pseudoTarget,
+ 'The effect.target of the scripted-generated animation is ' +
+ 'the same as the one from the argument of ' +
+ 'KeyframeEffectReadOnly constructor');
+ assert_equals(anims[0].effect.target, newAnim.effect.target,
+ 'Both the transition and the scripted-generated animation ' +
+ 'return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+ 'CSSPseudoElement object as that from the CSS generated transition');
+
done();
+
+