// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EventHandlerRegistry_h
#define EventHandlerRegistry_h

#include "core/CoreExport.h"
#include "core/frame/FrameHost.h"
#include "wtf/HashCountedSet.h"

namespace blink {

class AddEventListenerOptions;
class Document;
class EventTarget;

typedef HashCountedSet<UntracedMember<EventTarget>> EventTargetSet;

// Registry for keeping track of event handlers. Note that only handlers on
// documents that can be rendered or can receive input (i.e., are attached to a
// FrameHost) are registered here.
class CORE_EXPORT EventHandlerRegistry final : public GarbageCollectedFinalized<EventHandlerRegistry> {
public:
    explicit EventHandlerRegistry(FrameHost&);
    virtual ~EventHandlerRegistry();

    // Supported event handler classes. Note that each one may correspond to
    // multiple event types.
    enum EventHandlerClass {
        ScrollEvent,
        WheelEventBlocking,
        WheelEventPassive,
        TouchStartOrMoveEventBlocking,
        TouchStartOrMoveEventPassive,
        TouchEndOrCancelEventBlocking,
        TouchEndOrCancelEventPassive,
#if ENABLE(ASSERT)
        // Additional event categories for verifying handler tracking logic.
        EventsForTesting,
#endif
        EventHandlerClassCount, // Must be the last entry.
    };

    // Returns true if the FrameHost has event handlers of the specified class.
    bool hasEventHandlers(EventHandlerClass) const;

    // Returns a set of EventTargets which have registered handlers of the given class.
    const EventTargetSet* eventHandlerTargets(EventHandlerClass) const;

    // Registration and management of event handlers attached to EventTargets.
    void didAddEventHandler(EventTarget&, const AtomicString& eventType, const AddEventListenerOptions&);
    void didAddEventHandler(EventTarget&, EventHandlerClass);
    void didRemoveEventHandler(EventTarget&, const AtomicString& eventType, const AddEventListenerOptions&);
    void didRemoveEventHandler(EventTarget&, EventHandlerClass);
    void didRemoveAllEventHandlers(EventTarget&);

    void didMoveIntoFrameHost(EventTarget&);
    void didMoveOutOfFrameHost(EventTarget&);
    static void didMoveBetweenFrameHosts(EventTarget&, FrameHost* oldFrameHost, FrameHost* newFrameHost);

    // Either |documentDetached| or |didMove{Into,OutOf,Between}FrameHosts| must
    // be called whenever the FrameHost that is associated with a registered event
    // target changes. This ensures the registry does not end up with stale
    // references to handlers that are no longer related to it.
    void documentDetached(Document&);

    DECLARE_TRACE();
    void clearWeakMembers(Visitor*);

private:
    enum ChangeOperation {
        Add, // Add a new event handler.
        Remove, // Remove an existing event handler.
        RemoveAll // Remove any and all existing event handlers for a given target.
    };

    // Returns true if |eventType| belongs to a class this registry tracks.
    static bool eventTypeToClass(const AtomicString& eventType, const AddEventListenerOptions&, EventHandlerClass* result);

    // Returns true if the operation actually added a new target or completely
    // removed an existing one.
    bool updateEventHandlerTargets(ChangeOperation, EventHandlerClass, EventTarget*);

    // Called on the EventHandlerRegistry of the root Document to notify
    // clients when we have added the first handler or removed the last one for
    // a given event class. |hasActiveHandlers| can be used to distinguish
    // between the two cases.
    void notifyHasHandlersChanged(EventHandlerClass, bool hasActiveHandlers);

    // Called to notify clients whenever a single event handler target is
    // registered or unregistered. If several handlers are registered for the
    // same target, only the first registration will trigger this notification.
    void notifyDidAddOrRemoveEventHandlerTarget(EventHandlerClass);

    // Record a change operation to a given event handler class and notify any
    // parent registry and other clients accordingly.
    void updateEventHandlerOfType(ChangeOperation, const AtomicString& eventType, const AddEventListenerOptions&, EventTarget*);

    void updateEventHandlerInternal(ChangeOperation, EventHandlerClass, EventTarget*);

    void updateAllEventHandlers(ChangeOperation, EventTarget&);

    void checkConsistency() const;

    Member<FrameHost> m_frameHost;
    EventTargetSet m_targets[EventHandlerClassCount];
};

} // namespace blink

#endif // EventHandlerRegistry_h
