Bug 848291 - Update TransitionEvent to be compatible with the spec, r=dbaron
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Sat, 04 May 2013 17:41:20 +0300
changeset 130836 e1fac3f4bd838dc3300360492e7a06fb703229c2
parent 130835 04abf5e608f1822a17638a3603b2f685d2352fb3
child 130837 0ca8ab638f997b8f11b870fb5d15502ac15a3d5a
push id24634
push userphilringnalda@gmail.com
push dateSat, 04 May 2013 21:00:23 +0000
treeherderautoland@7ef3c04c7533 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs848291
milestone23.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 848291 - Update TransitionEvent to be compatible with the spec, r=dbaron
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMTransitionEvent.cpp
content/events/src/nsDOMTransitionEvent.h
dom/interfaces/events/nsIDOMTransitionEvent.idl
dom/webidl/TransitionEvent.webidl
layout/style/nsTransitionManager.cpp
layout/style/test/test_transitions_events.html
widget/nsGUIEvent.h
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -824,17 +824,18 @@ nsDOMEvent::DuplicatePrivateData()
       break;
     }
     case NS_TRANSITION_EVENT:
     {
       nsTransitionEvent* oldTransitionEvent =
         static_cast<nsTransitionEvent*>(mEvent);
       newEvent = new nsTransitionEvent(false, msg,
                                        oldTransitionEvent->propertyName,
-                                       oldTransitionEvent->elapsedTime);
+                                       oldTransitionEvent->elapsedTime,
+                                       oldTransitionEvent->pseudoElement);
       NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
       break;
     }
     case NS_ANIMATION_EVENT:
     {
       nsAnimationEvent* oldAnimationEvent =
         static_cast<nsAnimationEvent*>(mEvent);
       newEvent = new nsAnimationEvent(false, msg,
--- a/content/events/src/nsDOMTransitionEvent.cpp
+++ b/content/events/src/nsDOMTransitionEvent.cpp
@@ -10,17 +10,18 @@
 #include "nsIXPCScriptable.h"
 
 nsDOMTransitionEvent::nsDOMTransitionEvent(mozilla::dom::EventTarget* aOwner,
                                            nsPresContext *aPresContext,
                                            nsTransitionEvent *aEvent)
   : nsDOMEvent(aOwner, aPresContext,
                aEvent ? aEvent : new nsTransitionEvent(false, 0,
                                                        EmptyString(),
-                                                       0.0))
+                                                       0.0,
+                                                       EmptyString()))
 {
   if (aEvent) {
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
     mEvent->time = PR_Now();
   }
@@ -40,43 +41,68 @@ DOMCI_DATA(TransitionEvent, nsDOMTransit
 NS_INTERFACE_MAP_BEGIN(nsDOMTransitionEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTransitionEvent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TransitionEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMTransitionEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMTransitionEvent, nsDOMEvent)
 
+//static
+already_AddRefed<nsDOMTransitionEvent>
+nsDOMTransitionEvent::Constructor(const mozilla::dom::GlobalObject& aGlobal,
+                                  const nsAString& aType,
+                                  const mozilla::dom::TransitionEventInit& aParam,
+                                  mozilla::ErrorResult& aRv)
+{
+  nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.Get());
+  nsRefPtr<nsDOMTransitionEvent> e = new nsDOMTransitionEvent(t, nullptr, nullptr);
+  bool trusted = e->Init(t);
+  aRv = e->InitTransitionEvent(aType, aParam.mBubbles, aParam.mCancelable,
+                               aParam.mPropertyName, aParam.mElapsedTime,
+                               aParam.mPseudoElement);
+  e->SetTrusted(trusted);
+  return e.forget();
+}
+
 NS_IMETHODIMP
 nsDOMTransitionEvent::GetPropertyName(nsAString & aPropertyName)
 {
   aPropertyName = TransitionEvent()->propertyName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTransitionEvent::GetElapsedTime(float *aElapsedTime)
 {
   *aElapsedTime = ElapsedTime();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMTransitionEvent::GetPseudoElement(nsAString& aPseudoElement)
+{
+  aPseudoElement = TransitionEvent()->pseudoElement;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMTransitionEvent::InitTransitionEvent(const nsAString & typeArg,
                                           bool canBubbleArg,
                                           bool cancelableArg,
                                           const nsAString & propertyNameArg,
-                                          float elapsedTimeArg)
+                                          float elapsedTimeArg,
+                                          const nsAString& aPseudoElement)
 {
   nsresult rv = nsDOMEvent::InitEvent(typeArg, canBubbleArg, cancelableArg);
   NS_ENSURE_SUCCESS(rv, rv);
 
   TransitionEvent()->propertyName = propertyNameArg;
   TransitionEvent()->elapsedTime = elapsedTimeArg;
-
+  TransitionEvent()->pseudoElement = aPseudoElement;
   return NS_OK;
 }
 
 nsresult
 NS_NewDOMTransitionEvent(nsIDOMEvent **aInstancePtrResult,
                          mozilla::dom::EventTarget* aOwner,
                          nsPresContext *aPresContext,
                          nsTransitionEvent *aEvent)
--- a/content/events/src/nsDOMTransitionEvent.h
+++ b/content/events/src/nsDOMTransitionEvent.h
@@ -20,39 +20,48 @@ public:
                        nsPresContext *aPresContext,
                        nsTransitionEvent *aEvent);
   ~nsDOMTransitionEvent();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_TO_NSDOMEVENT
   NS_DECL_NSIDOMTRANSITIONEVENT
 
+  static already_AddRefed<nsDOMTransitionEvent>
+  Constructor(const mozilla::dom::GlobalObject& aGlobal,
+              const nsAString& aType,
+              const mozilla::dom::TransitionEventInit& aParam,
+              mozilla::ErrorResult& aRv);
+
   virtual JSObject* WrapObject(JSContext* aCx,
 			       JS::Handle<JSObject*> aScope) MOZ_OVERRIDE
   {
     return mozilla::dom::TransitionEventBinding::Wrap(aCx, aScope, this);
   }
 
   // xpidl implementation
   // GetPropertyName(nsAString& aPropertyName)
+  // GetPseudoElement(nsAString& aPreudoElement)
 
   float ElapsedTime()
   {
     return TransitionEvent()->elapsedTime;
   }
 
   void InitTransitionEvent(const nsAString& aType,
                            bool aCanBubble,
                            bool aCancelable,
                            const nsAString& aPropertyName,
                            float aElapsedTime,
+                           const mozilla::dom::Optional<nsAString>& aPseudoElement,
                            mozilla::ErrorResult& aRv)
   {
     aRv = InitTransitionEvent(aType, aCanBubble, aCancelable, aPropertyName,
-                              aElapsedTime);
+                              aElapsedTime, aPseudoElement.WasPassed() ?
+                                aPseudoElement.Value() : EmptyString());
   }
 private:
   nsTransitionEvent* TransitionEvent() {
     NS_ABORT_IF_FALSE(mEvent->eventStructType == NS_TRANSITION_EVENT,
                       "unexpected struct type");
     return static_cast<nsTransitionEvent*>(mEvent);
   }
 };
--- a/dom/interfaces/events/nsIDOMTransitionEvent.idl
+++ b/dom/interfaces/events/nsIDOMTransitionEvent.idl
@@ -6,18 +6,20 @@
 #include "nsIDOMEvent.idl"
 
 /**
  * Transition events are defined in:
  * http://www.w3.org/TR/css3-transitions/#transition-events-
  * http://dev.w3.org/csswg/css3-transitions/#transition-events-
  */
 
-[scriptable, builtinclass, uuid(9013310a-e376-40bc-b141-9b9ae3085daa)]
+[scriptable, builtinclass, uuid(eca50ac5-087a-4e50-bdf6-b341f0f9f8ab)]
 interface nsIDOMTransitionEvent : nsIDOMEvent {
   readonly attribute DOMString           propertyName;
   readonly attribute float               elapsedTime;
+  readonly attribute DOMString           pseudoElement;
   void               initTransitionEvent(in DOMString typeArg, 
                                          in boolean canBubbleArg, 
                                          in boolean cancelableArg, 
                                          in DOMString propertyNameArg,
-                                         in float elapsedTimeArg);
+                                         in float elapsedTimeArg,
+                                         [optional] in DOMString pseudoElement);
 };
--- a/dom/webidl/TransitionEvent.webidl
+++ b/dom/webidl/TransitionEvent.webidl
@@ -6,19 +6,32 @@
  * Transition events are defined in:
  * http://www.w3.org/TR/css3-transitions/#transition-events-
  * http://dev.w3.org/csswg/css3-transitions/#transition-events-
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+[Constructor(DOMString type, optional TransitionEventInit eventInitDict)]
 interface TransitionEvent : Event {
   readonly attribute DOMString propertyName;
   readonly attribute float     elapsedTime;
+  readonly attribute DOMString pseudoElement;
+};
 
+dictionary TransitionEventInit : EventInit {
+  DOMString propertyName = "";
+  float elapsedTime = 0;
+  DOMString pseudoElement = "";
+};
+
+// initTransitionEvent is a legacy method, and removed from the latest version
+// of the specification.
+partial interface TransitionEvent {
   [Throws]
   void initTransitionEvent(DOMString aType,
                            boolean aCanBubble,
                            boolean aCancelable,
                            DOMString aPropertyName,
-                           float aElapsedTime);
+                           float aElapsedTime,
+                           optional DOMString pseudoElement);
 };
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -962,30 +962,31 @@ nsTransitionManager::SizeOfIncludingThis
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
 struct TransitionEventInfo {
   nsCOMPtr<nsIContent> mElement;
   nsTransitionEvent mEvent;
 
   TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty,
-                      TimeDuration aDuration)
+                      TimeDuration aDuration, const nsAString& aPseudoElement)
     : mElement(aElement),
       mEvent(true, NS_TRANSITION_END,
              NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)),
-             aDuration.ToSeconds())
+             aDuration.ToSeconds(), aPseudoElement)
   {
   }
 
   // nsTransitionEvent doesn't support copy-construction, so we need
   // to ourselves in order to work with nsTArray
   TransitionEventInfo(const TransitionEventInfo &aOther)
     : mElement(aOther.mElement),
       mEvent(true, NS_TRANSITION_END,
-             aOther.mEvent.propertyName, aOther.mEvent.elapsedTime)
+             aOther.mEvent.propertyName, aOther.mEvent.elapsedTime,
+             aOther.mEvent.pseudoElement)
   {
   }
 };
 
 /* virtual */ void
 nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime)
 {
   NS_ABORT_IF_FALSE(mPresContext,
@@ -1043,28 +1044,31 @@ nsTransitionManager::FlushTransitions(Fl
           // completion. We only clear on a throttle-able cycle because that
           // means it is a regular restyle tick and thus it is safe to discard
           // the transition. If the flush is not throttle-able, we might still
           // have new transitions left to process. See comment below.
           if (aFlags == Can_Throttle) {
             et->mPropertyTransitions.RemoveElementAt(i);
           }
         } else if (pt.mStartTime + pt.mDuration <= now) {
-          // Fire transitionend events only for transitions on elements
-          // and not those on pseudo-elements, since we can't target an
-          // event at pseudo-elements.
-          if (et->mElementProperty == nsGkAtoms::transitionsProperty) {
-            nsCSSProperty prop = pt.mProperty;
-            if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_REPORT_OTHER_NAME))
-            {
-              prop = nsCSSProps::OtherNameFor(prop);
-            }
-            events.AppendElement(
-              TransitionEventInfo(et->mElement, prop, pt.mDuration));
+          nsCSSProperty prop = pt.mProperty;
+          if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_REPORT_OTHER_NAME))
+          {
+            prop = nsCSSProps::OtherNameFor(prop);
           }
+          nsIAtom* ep = et->mElementProperty;
+          NS_NAMED_LITERAL_STRING(before, "::before");
+          NS_NAMED_LITERAL_STRING(after, "::after");
+          events.AppendElement(
+            TransitionEventInfo(et->mElement, prop, pt.mDuration,
+                                ep == nsGkAtoms::transitionsProperty ?
+                                  EmptyString() :
+                                  ep == nsGkAtoms::transitionsOfBeforeProperty ?
+                                    before :
+                                    after));
 
           // Leave this transition in the list for one more refresh
           // cycle, since we haven't yet processed its style change, and
           // if we also have (already, or will have from processing
           // transitionend events or other refresh driver notifications)
           // a non-animation style change that would affect it, we need
           // to know not to start a new transition for the transition
           // from the almost-completed value to the final value.
--- a/layout/style/test/test_transitions_events.html
+++ b/layout/style/test/test_transitions_events.html
@@ -25,17 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 #four {
   /* give the reversing transition a long duration; the reversing will
      still be quick */
   transition-duration: 30s;
   transition-timing-function: cubic-bezier(0, 1, 1, 0);
 }
 
-/* make sure we don't get events for the pseudo-elements */
 #seven::before, #seven::after {
   content: "x";
   transition-duration: 50ms;
 }
 #seven[foo]::before, #seven[foo]::after { color: lime; }
 
 </style>
 <body>
@@ -75,16 +74,17 @@ var got_one_target_textdecorationcolor =
 var got_two_target = false;
 var got_three_top = false;
 var got_three_right = false;
 var got_three_bottom = false;
 var got_three_left = false;
 var got_four_root = false;
 var got_body = false;
 var did_stops = false;
+var got_before = false;
 
 document.documentElement.addEventListener("transitionend",
   function(event) {
     if (event.target == $("one")) {
       ok(!got_one_root, "transitionend on one on root");
       is(event.propertyName, "border-right-color",
          "propertyName for transitionend on one");
       is(event.elapsedTime, 0.5,
@@ -109,16 +109,25 @@ document.documentElement.addEventListene
       ok(!got_body, "transitionend on body on root");
       is(event.propertyName, "some-unknown-prop",
          "propertyName for transitionend on body");
       // Reported time should (really?) be shortened by reversing.
       is(event.elapsedTime, -4.75,
          "elapsedTime for transitionend on body");
       got_body = true;
       finished_test();
+    } else if (event.target == $("seven")) {
+      if (!got_before) {
+        got_before = true;
+        is(event.pseudoElement, "::before");
+      } else {
+        is(event.pseudoElement, "::after");
+      }
+      is(event.propertyName, "color");
+      is(event.isTrusted, true);
     } else {
       if (!did_stops &&
           (event.target == $("five") || event.target == $("six"))) {
         todo(false,
              "timeout to stop transitions firing later than it should be");
         return;
       }
       ok(false,
@@ -258,17 +267,16 @@ started_test();
 // We cancel the transition on five by changing 'transition-property',
 // and should thus get no event.
 $("five").style.color = "lime";
 
 // We cancel the transition on six by changing 'transition-duration' and
 // then changing the value, so we should get no event.
 $("six").style.color = "lime";
 
-// We should get no events from transitions on pseudo-elements.
 $("seven").setAttribute("foo", "bar");
 
 setTimeout(function() {
              if (cs("five") != "rgb(0, 255, 0)" &&
                  cs("six") != "rgb(0, 255, 0)") {
                // The transition hasn't finished already.
                did_stops = true;
              }
@@ -285,16 +293,32 @@ function poll_start_reversal() {
     // The forward transition has not started yet.
     setTimeout(poll_start_reversal, 20);
   }
 }
 setTimeout(poll_start_reversal, 200);
 
 // And make our own event to dispatch to the body.
 started_test();
+
+var e = new TransitionEvent("foo",
+                            {
+                              bubbles: true,
+                              cancelable: true,
+                              propertyName: "name",
+                              elapsedTime: 0.5,
+                              pseudoElement: "pseudo"
+                            });
+is(e.bubbles, true);
+is(e.cancelable, true);
+is(e.propertyName, "name");
+is(e.elapsedTime, "0.5");
+is(e.pseudoElement, "pseudo");
+is(e.isTrusted, false)
+
 var ev = document.createEvent("TransitionEvent"); // FIXME: un-specified
 ev.initTransitionEvent("transitionend", true, true,
                        "some-unknown-prop", -4.75);
 document.body.dispatchEvent(ev);
 
 </script>
 </pre>
 </body>
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -1753,24 +1753,27 @@ public:
   double delta;               // Delta for magnify and rotate events
   uint32_t clickCount;        // The number of taps for tap events
 };
 
 class nsTransitionEvent : public nsEvent
 {
 public:
   nsTransitionEvent(bool isTrusted, uint32_t msg,
-                    const nsString &propertyNameArg, float elapsedTimeArg)
+                    const nsAString& propertyNameArg, float elapsedTimeArg,
+                    const nsAString& pseudoElementArg)
     : nsEvent(isTrusted, msg, NS_TRANSITION_EVENT),
-      propertyName(propertyNameArg), elapsedTime(elapsedTimeArg)
+      propertyName(propertyNameArg), elapsedTime(elapsedTimeArg),
+      pseudoElement(pseudoElementArg)
   {
   }
 
   nsString propertyName;
   float elapsedTime;
+  nsString pseudoElement;
 };
 
 class nsAnimationEvent : public nsEvent
 {
 public:
   nsAnimationEvent(bool isTrusted, uint32_t msg,
                    const nsString &animationNameArg, float elapsedTimeArg)
     : nsEvent(isTrusted, msg, NS_ANIMATION_EVENT),