[ContentChangeObserver] Add support for observing implicit transitions
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Mar 2019 18:33:11 +0000 (18:33 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Mar 2019 18:33:11 +0000 (18:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195914
<rdar://problem/49091959>

Reviewed by Simon Fraser.

This patch is in preparation for observing elements with property "left" implicit transitions.

This is not a continuous tracking, we are only interested in the start and the end state.
The idea here is to register hidden elements only and check if they become visible by
the end of the transition (and ignore if the transition gets "canceled").

* page/animation/AnimationBase.h:
* page/animation/ImplicitAnimation.cpp:
(WebCore::ImplicitAnimation::ImplicitAnimation):
(WebCore::ImplicitAnimation::~ImplicitAnimation):
(WebCore::ImplicitAnimation::clear):
(WebCore::ImplicitAnimation::onAnimationEnd):
* page/animation/ImplicitAnimation.h:
* page/ios/ContentChangeObserver.cpp:
(WebCore::ContentChangeObserver::didAddTransition):
(WebCore::ContentChangeObserver::removeTransitionIfNeeded):
(WebCore::ContentChangeObserver::didFinishTransition):
(WebCore::ContentChangeObserver::didRemoveTransition):
(WebCore::ContentChangeObserver::didInstallDOMTimer):
* page/ios/ContentChangeObserver.h:
(WebCore::ContentChangeObserver::isObservingTransitions const):
(WebCore::ContentChangeObserver::isObservedPropertyForTransition const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243304 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/page/animation/AnimationBase.h
Source/WebCore/page/animation/ImplicitAnimation.cpp
Source/WebCore/page/animation/ImplicitAnimation.h
Source/WebCore/page/ios/ContentChangeObserver.cpp
Source/WebCore/page/ios/ContentChangeObserver.h

index 4d7c5ef..7def03c 100644 (file)
@@ -1,3 +1,34 @@
+2019-03-21  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Add support for observing implicit transitions
+        https://bugs.webkit.org/show_bug.cgi?id=195914
+        <rdar://problem/49091959>
+
+        Reviewed by Simon Fraser.
+
+        This patch is in preparation for observing elements with property "left" implicit transitions.
+
+        This is not a continuous tracking, we are only interested in the start and the end state.
+        The idea here is to register hidden elements only and check if they become visible by
+        the end of the transition (and ignore if the transition gets "canceled").
+
+        * page/animation/AnimationBase.h:
+        * page/animation/ImplicitAnimation.cpp:
+        (WebCore::ImplicitAnimation::ImplicitAnimation):
+        (WebCore::ImplicitAnimation::~ImplicitAnimation):
+        (WebCore::ImplicitAnimation::clear):
+        (WebCore::ImplicitAnimation::onAnimationEnd):
+        * page/animation/ImplicitAnimation.h:
+        * page/ios/ContentChangeObserver.cpp:
+        (WebCore::ContentChangeObserver::didAddTransition):
+        (WebCore::ContentChangeObserver::removeTransitionIfNeeded):
+        (WebCore::ContentChangeObserver::didFinishTransition):
+        (WebCore::ContentChangeObserver::didRemoveTransition):
+        (WebCore::ContentChangeObserver::didInstallDOMTimer):
+        * page/ios/ContentChangeObserver.h:
+        (WebCore::ContentChangeObserver::isObservingTransitions const):
+        (WebCore::ContentChangeObserver::isObservedPropertyForTransition const):
+
 2019-03-21  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Page: lazily create the agent
index f7abbaf..b635cb2 100644 (file)
@@ -61,7 +61,7 @@ public:
     Element* element() const { return m_element.get(); }
     const RenderStyle& currentStyle() const override;
     RenderElement* renderer() const override;
-    void clear();
+    virtual void clear();
 
     double duration() const;
 
index 4334e30..4f7c13e 100644 (file)
@@ -32,6 +32,9 @@
 #include "CSSAnimationControllerPrivate.h"
 #include "CSSPropertyAnimation.h"
 #include "CompositeAnimation.h"
+#if PLATFORM(IOS_FAMILY)
+#include "ContentChangeObserver.h"
+#endif
 #include "EventNames.h"
 #include "GeometryUtilities.h"
 #include "KeyframeAnimation.h"
@@ -46,11 +49,18 @@ ImplicitAnimation::ImplicitAnimation(const Animation& transition, CSSPropertyID
     , m_transitionProperty(transition.property())
     , m_animatingProperty(animatingProperty)
 {
+#if PLATFORM(IOS_FAMILY)
+    element.document().contentChangeObserver().didAddTransition(element, transition);
+#endif
     ASSERT(animatingProperty != CSSPropertyInvalid);
 }
 
 ImplicitAnimation::~ImplicitAnimation()
 {
+#if PLATFORM(IOS_FAMILY)
+    if (auto* element = this->element())
+        element->document().contentChangeObserver().didRemoveTransition(*element, m_animatingProperty);
+#endif
     // // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed.
     if (!postActive())
         endAnimation();
@@ -158,6 +168,15 @@ void ImplicitAnimation::pauseAnimation(double timeOffset)
         setNeedsStyleRecalc(element());
 }
 
+void ImplicitAnimation::clear()
+{
+#if PLATFORM(IOS_FAMILY)
+    if (auto* element = this->element())
+        element->document().contentChangeObserver().didRemoveTransition(*element, m_animatingProperty);
+#endif
+    AnimationBase::clear();
+}
+
 void ImplicitAnimation::endAnimation(bool)
 {
     if (auto* renderer = this->renderer())
@@ -166,6 +185,10 @@ void ImplicitAnimation::endAnimation(bool)
 
 void ImplicitAnimation::onAnimationEnd(double elapsedTime)
 {
+#if PLATFORM(IOS_FAMILY)
+    if (auto* element = this->element())
+        element->document().contentChangeObserver().didFinishTransition(*element, m_animatingProperty);
+#endif
     // If we have a keyframe animation on this property, this transition is being overridden. The keyframe
     // animation keeps an unanimated style in case a transition starts while the keyframe animation is
     // running. But now that the transition has completed, we need to update this style with its new
index 2f279a5..72d4cbf 100644 (file)
@@ -79,6 +79,8 @@ public:
 
     const RenderStyle& unanimatedStyle() const override { return *m_fromStyle; }
 
+    void clear() override;
+
 protected:
     bool shouldSendEventForListener(Document::ListenerType) const;    
     bool sendTransitionEvent(const AtomicString&, double elapsedTime);
index 8bde2d3..9b7c728 100644 (file)
@@ -39,6 +39,9 @@
 
 namespace WebCore {
 
+static const Seconds maximumDelayForTimers { 300_ms };
+static const Seconds maximumDelayForTransitions { 300_ms };
+
 ContentChangeObserver::ContentChangeObserver(Document& document)
     : m_document(document)
     , m_contentObservationTimer([this] { completeDurationBasedContentObservation(); })
@@ -81,13 +84,54 @@ void ContentChangeObserver::completeDurationBasedContentObservation()
     adjustObservedState(Event::EndedFixedObservationTimeWindow);
 }
 
+void ContentChangeObserver::didAddTransition(const Element& element, const Animation& transition)
+{
+    if (!m_document.settings().contentChangeObserverEnabled())
+        return;
+    if (hasVisibleChangeState())
+        return;
+    if (!isObservingTransitions())
+        return;
+    if (!transition.isDurationSet() || !transition.isPropertySet())
+        return;
+    if (!isObservedPropertyForTransition(transition.property()))
+        return;
+    auto transitionEnd = Seconds { transition.duration() + std::max<double>(0, transition.isDelaySet() ? transition.delay() : 0) };
+    if (transitionEnd > maximumDelayForTransitions)
+        return;
+    LOG_WITH_STREAM(ContentObservation, stream << "didAddTransition: transition created on " << &element << " (" << transitionEnd.milliseconds() << "ms).");
+
+    m_elementsWithTransition.add(&element);
+    // FIXME: report state change.
+}
+
+void ContentChangeObserver::didFinishTransition(const Element& element, CSSPropertyID propertyID)
+{
+    if (!isObservedPropertyForTransition(propertyID))
+        return;
+    if (!m_elementsWithTransition.take(&element))
+        return;
+    LOG_WITH_STREAM(ContentObservation, stream << "didFinishTransition: transition finished (" << &element << ").");
+    // FIXME: report state change.
+}
+
+void ContentChangeObserver::didRemoveTransition(const Element& element, CSSPropertyID propertyID)
+{
+    if (!isObservedPropertyForTransition(propertyID))
+        return;
+    if (!m_elementsWithTransition.take(&element))
+        return;
+    LOG_WITH_STREAM(ContentObservation, stream << "didRemoveTransition: transition got interrupted (" << &element << ").");
+    // FIXME: report state change.
+}
+
 void ContentChangeObserver::didInstallDOMTimer(const DOMTimer& timer, Seconds timeout, bool singleShot)
 {
     if (!m_document.settings().contentChangeObserverEnabled())
         return;
     if (m_document.activeDOMObjectsAreSuspended())
         return;
-    if (timeout > 300_ms || !singleShot)
+    if (timeout > maximumDelayForTimers || !singleShot)
         return;
     if (!isObservingDOMTimerScheduling())
         return;
index 484396d..8eb0b05 100644 (file)
 #include "PlatformEvent.h"
 #include "Timer.h"
 #include "WKContentObservation.h"
+#include <wtf/HashSet.h>
 
 namespace WebCore {
 
+class Animation;
 class DOMTimer;
 class Document;
 class Element;
@@ -46,6 +48,11 @@ public:
 
     void didInstallDOMTimer(const DOMTimer&, Seconds timeout, bool singleShot);
     void didRemoveDOMTimer(const DOMTimer&);
+
+    void didAddTransition(const Element&, const Animation&);
+    void didFinishTransition(const Element&, CSSPropertyID);
+    void didRemoveTransition(const Element&, CSSPropertyID);
+
     WEBCORE_EXPORT void willNotProceedWithClick();
     WEBCORE_EXPORT static void didRecognizeLongPress(Frame& mainFrame);
     WEBCORE_EXPORT static void didPreventDefaultForEvent(Frame& mainFrame);
@@ -114,6 +121,8 @@ private:
 
     void setShouldObserveDOMTimerScheduling(bool observe) { m_isObservingDOMTimerScheduling = observe; }
     bool isObservingDOMTimerScheduling() const { return m_isObservingDOMTimerScheduling; }
+    bool isObservingTransitions() const { return m_isObservingTransitions; }
+    bool isObservedPropertyForTransition(CSSPropertyID propertyId) const { return propertyId == CSSPropertyLeft; }
     void domTimerExecuteDidStart(const DOMTimer&);
     void domTimerExecuteDidFinish(const DOMTimer&);
     void registerDOMTimer(const DOMTimer& timer) { m_DOMTimerList.add(&timer); }
@@ -167,6 +176,8 @@ private:
     Document& m_document;
     Timer m_contentObservationTimer;
     HashSet<const DOMTimer*> m_DOMTimerList;
+    // FIXME: Move over to WeakHashSet when it starts supporting const.
+    HashSet<const Element*> m_elementsWithTransition;
     bool m_touchEventIsBeingDispatched { false };
     bool m_isWaitingForStyleRecalc { false };
     bool m_isInObservedStyleRecalc { false };
@@ -174,6 +185,7 @@ private:
     bool m_observedDomTimerIsBeingExecuted { false };
     bool m_mouseMovedEventIsBeingDispatched { false };
     bool m_isBetweenTouchEndAndMouseMoved { false };
+    bool m_isObservingTransitions { false };
 };
 
 inline void ContentChangeObserver::setHasNoChangeState()