blob: 3fb6a83b362c151480e7fbc22fc5326c7de9b389 [file] [log] [blame]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if ENABLE(CONTENT_CHANGE_OBSERVER)
#include "CSSPropertyNames.h"
#include "Document.h"
#include "Element.h"
#include "PlatformEvent.h"
#include "RenderStyleConstants.h"
#include "Timer.h"
#include "WKContentObservation.h"
#include <wtf/HashSet.h>
#include <wtf/Seconds.h>
#include <wtf/WeakPtr.h>
namespace WebCore {
class Animation;
class DOMTimer;
class Element;
class ContentChangeObserver : public CanMakeWeakPtr<ContentChangeObserver> {
WTF_MAKE_FAST_ALLOCATED;
public:
ContentChangeObserver(Document&);
WEBCORE_EXPORT void startContentObservationForDuration(Seconds duration);
WEBCORE_EXPORT void stopContentObservation();
WKContentChange observedContentChange() const { return m_observedContentState; }
WEBCORE_EXPORT static bool isConsideredVisible(const Node&);
static bool isVisuallyHidden(const Node&);
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 static void didRecognizeLongPress(Frame& mainFrame);
WEBCORE_EXPORT static void didPreventDefaultForEvent(Frame& mainFrame);
WEBCORE_EXPORT static void didCancelPotentialTap(Frame& mainFrame);
void didSuspendActiveDOMObjects();
void willDetachPage();
void rendererWillBeDestroyed(const Element&);
void willNotProceedWithClick();
void willNotProceedWithFixedObservationTimeWindow();
void setHiddenTouchTarget(Element& targetElement) { m_hiddenTouchTargetElement = targetElement; }
void resetHiddenTouchTarget() { m_hiddenTouchTargetElement = { }; }
Element* hiddenTouchTarget() const { return m_hiddenTouchTargetElement.get(); }
class StyleChangeScope {
public:
StyleChangeScope(Document&, const Element&);
~StyleChangeScope();
private:
ContentChangeObserver& m_contentChangeObserver;
const Element& m_element;
std::optional<bool> m_wasHidden;
bool m_hadRenderer { false };
};
class TouchEventScope {
public:
WEBCORE_EXPORT TouchEventScope(Document&, PlatformEvent::Type);
WEBCORE_EXPORT ~TouchEventScope();
private:
ContentChangeObserver& m_contentChangeObserver;
};
class MouseMovedScope {
public:
WEBCORE_EXPORT MouseMovedScope(Document&);
WEBCORE_EXPORT ~MouseMovedScope();
private:
ContentChangeObserver& m_contentChangeObserver;
};
class StyleRecalcScope {
public:
StyleRecalcScope(Document&);
~StyleRecalcScope();
private:
ContentChangeObserver& m_contentChangeObserver;
};
class DOMTimerScope {
public:
DOMTimerScope(Document*, const DOMTimer&);
~DOMTimerScope();
private:
ContentChangeObserver* m_contentChangeObserver { nullptr };
const DOMTimer& m_domTimer;
};
private:
void touchEventDidStart(PlatformEvent::Type);
void touchEventDidFinish();
void mouseMovedDidStart();
void mouseMovedDidFinish();
void didRecognizeLongPress();
void elementDidBecomeVisible(const Element&);
void elementDidBecomeHidden(const Element&);
void setShouldObserveDOMTimerSchedulingAndTransitions(bool);
bool isObservingDOMTimerScheduling() const { return m_isObservingDOMTimerScheduling; }
bool isObservingTransitions() const { return m_isObservingTransitions; }
bool isObservedPropertyForTransition(CSSPropertyID propertyId) const { return propertyId == CSSPropertyLeft || propertyId == CSSPropertyOpacity; }
void domTimerExecuteDidStart(const DOMTimer&);
void domTimerExecuteDidFinish(const DOMTimer&);
void registerDOMTimer(const DOMTimer& timer) { m_DOMTimerList.add(&timer); }
void unregisterDOMTimer(const DOMTimer& timer) { m_DOMTimerList.remove(&timer); }
void clearObservedDOMTimers() { m_DOMTimerList.clear(); }
void clearObservedTransitions() { m_elementsWithTransition.clear(); }
bool containsObservedDOMTimer(const DOMTimer& timer) const { return m_DOMTimerList.contains(&timer); }
void styleRecalcDidStart();
void styleRecalcDidFinish();
void setShouldObserveNextStyleRecalc(bool);
bool isWaitingForStyleRecalc() const { return m_isWaitingForStyleRecalc; }
bool isObservingContentChanges() const;
void stopObservingPendingActivities();
void reset();
void setHasNoChangeState() { m_observedContentState = WKContentNoChange; }
void setHasIndeterminateState() { m_observedContentState = WKContentIndeterminateChange; }
void setHasVisibleChangeState() { m_observedContentState = WKContentVisibilityChange; }
bool hasVisibleChangeState() const { return observedContentChange() == WKContentVisibilityChange; }
bool hasObservedDOMTimer() const { return !m_DOMTimerList.isEmpty(); }
bool hasObservedTransition() const { return !m_elementsWithTransition.isEmpty(); }
void setIsBetweenTouchEndAndMouseMoved(bool isBetween) { m_isBetweenTouchEndAndMouseMoved = isBetween; }
bool isBetweenTouchEndAndMouseMoved() const { return m_isBetweenTouchEndAndMouseMoved; }
void setTouchEventIsBeingDispatched(bool dispatching) { m_touchEventIsBeingDispatched = dispatching; }
bool isTouchEventBeingDispatched() const { return m_touchEventIsBeingDispatched; }
void setMouseMovedEventIsBeingDispatched(bool dispatching) { m_mouseMovedEventIsBeingDispatched = dispatching; }
bool isMouseMovedEventBeingDispatched() const { return m_mouseMovedEventIsBeingDispatched; }
bool hasPendingActivity() const { return hasObservedDOMTimer() || hasObservedTransition() || m_isWaitingForStyleRecalc || isObservationTimeWindowActive(); }
bool isObservationTimeWindowActive() const { return m_contentObservationTimer.isActive(); }
void completeDurationBasedContentObservation();
bool visibleRendererWasDestroyed(const Element& element) const { return m_elementsWithDestroyedVisibleRenderer.contains(&element); }
bool shouldObserveVisibilityChangeForElement(const Element&);
enum class ElementHadRenderer { No, Yes };
bool isConsideredActionableContent(const Element&, ElementHadRenderer) const;
enum class Event {
StartedTouchStartEventDispatching,
EndedTouchStartEventDispatching,
WillNotProceedWithClick,
StartedMouseMovedEventDispatching,
EndedMouseMovedEventDispatching,
InstalledDOMTimer,
RemovedDOMTimer,
StartedDOMTimerExecution,
EndedDOMTimerExecution,
StartedStyleRecalc,
EndedStyleRecalc,
AddedTransition,
EndedTransitionButFinalStyleIsNotDefiniteYet,
CompletedTransition,
CanceledTransition,
StartedFixedObservationTimeWindow,
EndedFixedObservationTimeWindow,
WillNotProceedWithFixedObservationTimeWindow,
ElementDidBecomeVisible
};
void adjustObservedState(Event);
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;
HashSet<const Element*> m_elementsWithDestroyedVisibleRenderer;
WKContentChange m_observedContentState { WKContentNoChange };
WeakPtr<Element> m_hiddenTouchTargetElement;
WeakHashSet<Element> m_visibilityCandidateList;
bool m_touchEventIsBeingDispatched { false };
bool m_isWaitingForStyleRecalc { false };
bool m_isInObservedStyleRecalc { false };
bool m_isObservingDOMTimerScheduling { false };
bool m_observedDomTimerIsBeingExecuted { false };
bool m_mouseMovedEventIsBeingDispatched { false };
bool m_isBetweenTouchEndAndMouseMoved { false };
bool m_isObservingTransitions { false };
};
inline bool ContentChangeObserver::isObservingContentChanges() const
{
return isTouchEventBeingDispatched()
|| isBetweenTouchEndAndMouseMoved()
|| isMouseMovedEventBeingDispatched()
|| m_observedDomTimerIsBeingExecuted
|| m_isInObservedStyleRecalc
|| isObservationTimeWindowActive();
}
inline void ContentChangeObserver::setShouldObserveDOMTimerSchedulingAndTransitions(bool observe)
{
m_isObservingDOMTimerScheduling = observe;
m_isObservingTransitions = observe;
}
}
#endif // ENABLE(CONTENT_CHANGE_OBSERVER)