Bug 1174575 - Part 3: Implement KeyframeEffectReadOnly::GetTarget(). r=birtles
authorBoris Chiou <boris.chiou@gmail.com>
Mon, 01 Feb 2016 23:59:00 +0100
changeset 283622 db640b0bb03575bffca6721d204957c4b84b349a
parent 283621 acd70f0308ecf867e1de65f279aff8bf1a37ff32
child 283623 b8a9555e8d4db68318dfcccafb49e9db14af4f67
push id29988
push usercbook@mozilla.com
push dateWed, 10 Feb 2016 10:47:59 +0000
treeherdermozilla-central@7042e8a19f94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1174575
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1174575 - Part 3: Implement KeyframeEffectReadOnly::GetTarget(). r=birtles Implement GetTarget() and functions of CSSPseudoElement. We use a strong reference from CSSPseudoElement to Element and a non-owning reference from Element to CSSPseudoElement.
dom/animation/CSSPseudoElement.cpp
dom/animation/CSSPseudoElement.h
dom/animation/KeyframeEffect.cpp
dom/base/nsGkAtomList.h
--- a/dom/animation/CSSPseudoElement.cpp
+++ b/dom/animation/CSSPseudoElement.cpp
@@ -1,25 +1,46 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CSSPseudoElement.h"
 #include "mozilla/dom/CSSPseudoElementBinding.h"
+#include "mozilla/dom/Element.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(CSSPseudoElement)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CSSPseudoElement, mParentElement)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CSSPseudoElement, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CSSPseudoElement, Release)
 
+CSSPseudoElement::CSSPseudoElement(Element* aElement,
+                                   nsCSSPseudoElements::Type aType)
+  : mParentElement(aElement)
+  , mPseudoType(aType)
+{
+  MOZ_ASSERT(aElement);
+  MOZ_ASSERT(aType == nsCSSPseudoElements::ePseudo_after ||
+             aType == nsCSSPseudoElements::ePseudo_before,
+             "Unexpected Pseudo Type");
+}
+
+CSSPseudoElement::~CSSPseudoElement()
+{
+  // Element might have been unlinked already, so we have to do null check.
+  if (mParentElement) {
+    mParentElement->DeleteProperty(
+      GetCSSPseudoElementPropertyAtom(mPseudoType));
+  }
+}
+
 JSObject*
 CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return CSSPseudoElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 CSSPseudoElement::GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal)
@@ -35,10 +56,56 @@ CSSPseudoElement::Animate(
     const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
     ErrorResult& aError)
 {
   // Bug 1241784: Implement this API.
   NS_NOTREACHED("CSSPseudoElement::Animate() is not implemented yet.");
   return nullptr;
 }
 
+/* static */ already_AddRefed<CSSPseudoElement>
+CSSPseudoElement::GetCSSPseudoElement(Element* aElement,
+                                      nsCSSPseudoElements::Type aType)
+{
+  if (!aElement) {
+    return nullptr;
+  }
+
+  nsIAtom* propName = CSSPseudoElement::GetCSSPseudoElementPropertyAtom(aType);
+  RefPtr<CSSPseudoElement> pseudo =
+    static_cast<CSSPseudoElement*>(aElement->GetProperty(propName));
+  if (pseudo) {
+    return pseudo.forget();
+  }
+
+  // CSSPseudoElement is a purely external interface created on-demand, and
+  // when all references from script to the pseudo are dropped, we can drop the
+  // CSSPseudoElement object, so use a non-owning reference from Element to
+  // CSSPseudoElement.
+  pseudo = new CSSPseudoElement(aElement, aType);
+  nsresult rv = aElement->SetProperty(propName, pseudo, nullptr, true);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("SetProperty failed");
+    return nullptr;
+  }
+  return pseudo.forget();
+}
+
+/* static */ nsIAtom*
+CSSPseudoElement::GetCSSPseudoElementPropertyAtom(
+    nsCSSPseudoElements::Type aType)
+{
+  switch (aType) {
+    case nsCSSPseudoElements::ePseudo_before:
+      return nsGkAtoms::cssPseudoElementBeforeProperty;
+
+    case nsCSSPseudoElements::ePseudo_after:
+      return nsGkAtoms::cssPseudoElementAfterProperty;
+
+    default:
+      NS_NOTREACHED("Should not try to get CSSPseudoElement "
+                    "other than ::before or ::after");
+      return nullptr;
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/CSSPseudoElement.h
+++ b/dom/animation/CSSPseudoElement.h
@@ -6,51 +6,82 @@
 
 #ifndef mozilla_dom_CSSPseudoElement_h
 #define mozilla_dom_CSSPseudoElement_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/RefPtr.h"
+#include "nsCSSPseudoElements.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class Animation;
 class Element;
 class UnrestrictedDoubleOrKeyframeAnimationOptions;
 
 class CSSPseudoElement final : public nsWrapperCache
 {
 public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CSSPseudoElement)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CSSPseudoElement)
 
 protected:
-  virtual ~CSSPseudoElement() = default;
+  virtual ~CSSPseudoElement();
 
 public:
   ParentObject GetParentObject() const
   {
-    // This will be implemented in later patch.
-    return ParentObject(nullptr, nullptr);
+    return mParentElement->GetParentObject();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
-  void GetType(nsString& aRetVal) const { }
-  already_AddRefed<Element> ParentElement() const { return nullptr; }
+  void GetType(nsString& aRetVal) const
+  {
+    MOZ_ASSERT(nsCSSPseudoElements::GetPseudoAtom(mPseudoType),
+               "All pseudo-types allowed by this class should have a"
+               " corresponding atom");
+    nsCSSPseudoElements::GetPseudoAtom(mPseudoType)->ToString(aRetVal);
+  }
+  already_AddRefed<Element> ParentElement() const
+  {
+    RefPtr<Element> retVal(mParentElement);
+    return retVal.forget();
+  }
 
   void GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal);
   already_AddRefed<Animation>
     Animate(JSContext* aContext,
             JS::Handle<JSObject*> aFrames,
             const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
             ErrorResult& aError);
+
+  // Given an element:pseudoType pair, returns the CSSPseudoElement stored as a
+  // property on |aElement|. If there is no CSSPseudoElement for the specified
+  // pseudo-type on element, a new CSSPseudoElement will be created and stored
+  // on the element.
+  static already_AddRefed<CSSPseudoElement>
+    GetCSSPseudoElement(Element* aElement, nsCSSPseudoElements::Type aType);
+
+private:
+  // Only ::before and ::after are supported.
+  CSSPseudoElement(Element* aElement, nsCSSPseudoElements::Type aType);
+
+  static nsIAtom*
+  GetCSSPseudoElementPropertyAtom(nsCSSPseudoElements::Type aType);
+
+  // mParentElement needs to be an owning reference since if script is holding
+  // on to the pseudo-element, it needs to continue to be able to refer to
+  // the parent element.
+  RefPtr<Element> mParentElement;
+  nsCSSPseudoElements::Type mPseudoType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CSSPseudoElement_h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1658,23 +1658,36 @@ KeyframeEffectReadOnly::Constructor(
   effect->mProperties = Move(animationProperties);
   return effect.forget();
 }
 
 void
 KeyframeEffectReadOnly::GetTarget(
     Nullable<OwningElementOrCSSPseudoElement>& aRv) const
 {
-  // Currently we never return animations from the API whose effect
-  // targets a pseudo-element so this should never be called when
-  // mPseudoType is not 'none' (see bug 1174575).
-  MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
-             "Requesting the target of a KeyframeEffect that targets a"
-             " pseudo-element is not yet supported.");
-  aRv.Value().SetAsElement() = mTarget;
+  if (!mTarget) {
+    aRv.SetNull();
+    return;
+  }
+
+  switch (mPseudoType) {
+    case nsCSSPseudoElements::ePseudo_before:
+    case nsCSSPseudoElements::ePseudo_after:
+      aRv.SetValue().SetAsCSSPseudoElement() =
+        CSSPseudoElement::GetCSSPseudoElement(mTarget, mPseudoType);
+      break;
+
+    case nsCSSPseudoElements::ePseudo_NotPseudoElement:
+      aRv.SetValue().SetAsElement() = mTarget;
+      break;
+
+    default:
+      NS_NOTREACHED("Animation of unsupported pseudo-type");
+      aRv.SetNull();
+  }
 }
 
 void
 KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
                                   nsTArray<JSObject*>& aResult,
                                   ErrorResult& aRv)
 {
   nsTArray<OrderedKeyframeValueEntry> entries;
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2107,16 +2107,18 @@ GK_ATOM(ongamepaddisconnected, "ongamepa
 
 // Content property names
 GK_ATOM(animationsProperty, "AnimationsProperty")        // FrameAnimations*
 GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations*
 GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
 GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet*
 GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet*
 GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
+GK_ATOM(cssPseudoElementBeforeProperty, "CSSPseudoElementBeforeProperty") // CSSPseudoElement*
+GK_ATOM(cssPseudoElementAfterProperty, "CSSPseudoElementAfterProperty") // CSSPseudoElement*
 GK_ATOM(transitionsProperty, "TransitionsProperty")        // FrameTransitions*
 GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
 GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
 GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
 GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
 GK_ATOM(baseURIProperty, "baseURIProperty")
 GK_ATOM(lockedStyleStates, "lockedStyleStates")
 GK_ATOM(apzCallbackTransform, "apzCallbackTransform")