| Index: Source/core/dom/EventHandlerRegistry.cpp
|
| diff --git a/Source/core/dom/EventHandlerRegistry.cpp b/Source/core/dom/EventHandlerRegistry.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d547c80d52a0ce250d731e54f5d7165a0d61bb44
|
| --- /dev/null
|
| +++ b/Source/core/dom/EventHandlerRegistry.cpp
|
| @@ -0,0 +1,358 @@
|
| +// 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.
|
| +#include "config.h"
|
| +#include "core/dom/EventHandlerRegistry.h"
|
| +
|
| +#include "core/dom/Document.h"
|
| +#include "core/events/ThreadLocalEventNames.h"
|
| +#include "core/events/WheelEvent.h"
|
| +#include "core/frame/FrameHost.h"
|
| +#include "core/frame/LocalFrame.h"
|
| +#include "core/page/Chrome.h"
|
| +#include "core/page/ChromeClient.h"
|
| +#include "core/page/Page.h"
|
| +#include "core/page/scrolling/ScrollingCoordinator.h"
|
| +
|
| +namespace WebCore {
|
| +
|
| +EventHandlerRegistry::WindowObserver::WindowObserver(EventHandlerRegistry& registry, DOMWindow& window)
|
| + : DOMWindowLifecycleObserver(&window)
|
| + , m_registry(registry)
|
| +{
|
| +}
|
| +
|
| +EventHandlerRegistry::WindowObserver::~WindowObserver()
|
| +{
|
| +}
|
| +
|
| +void EventHandlerRegistry::WindowObserver::didAddEventListener(DOMWindow* window, const AtomicString& eventType)
|
| +{
|
| + m_registry.didAddEventHandler(*window, eventType);
|
| +}
|
| +
|
| +void EventHandlerRegistry::WindowObserver::didRemoveEventListener(DOMWindow* window, const AtomicString& eventType)
|
| +{
|
| + m_registry.didRemoveEventHandler(*window, eventType);
|
| +}
|
| +
|
| +void EventHandlerRegistry::WindowObserver::didRemoveAllEventListeners(DOMWindow* window)
|
| +{
|
| + m_registry.didRemoveAllEventHandlers(*window);
|
| +}
|
| +
|
| +EventHandlerRegistry::DocumentObserver::DocumentObserver(Document& document)
|
| + : DocumentLifecycleObserver(&document) { }
|
| +
|
| +EventHandlerRegistry::DocumentObserver::~DocumentObserver()
|
| +{
|
| +}
|
| +void EventHandlerRegistry::DocumentObserver::documentWasAttached()
|
| +{
|
| + Document* document = lifecycleContext();
|
| + if (document->parentDocument())
|
| + return;
|
| + EventHandlerRegistry* registry = EventHandlerRegistry::from(*document);
|
| +
|
| + // Make sure we are tracking handlers on the window.
|
| + if (!registry->m_windowObserver && document->domWindow())
|
| + registry->m_windowObserver = adoptPtr(new WindowObserver(*registry, *document->domWindow()));
|
| +
|
| + // When the root document is attached, notify clients of any handlers it has.
|
| + for (size_t i = 0; i < EventHandlerClassCount; ++i) {
|
| + EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
|
| + if (registry->hasEventHandlers(handlerClass)) {
|
| + registry->notifyHasHandlersChanged(handlerClass, true);
|
| + registry->notifyDidAddOrRemoveEventHandlerTarget(handlerClass);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void EventHandlerRegistry::DocumentObserver::willDetachDocument()
|
| +{
|
| + Document* document = lifecycleContext();
|
| +
|
| + // The detached document should no longer track handlers on its window.
|
| + EventHandlerRegistry* registry = EventHandlerRegistry::from(*document);
|
| + if (document->domWindow())
|
| + registry->didRemoveAllEventHandlers(*document->domWindow());
|
| + registry->m_windowObserver.clear();
|
| +
|
| + // Unregister our Document node from the parent for all event classes that
|
| + // have handlers.
|
| + Document* parentDocument = document->parentDocument();
|
| + if (!parentDocument)
|
| + return;
|
| + EventHandlerRegistry* parentRegistry = EventHandlerRegistry::from(*parentDocument);
|
| + parentRegistry->didRemoveAllEventHandlers(*document);
|
| +}
|
| +
|
| +EventHandlerRegistry::HandlerState::HandlerState()
|
| + : externalHandlerCount(0)
|
| +{
|
| +}
|
| +
|
| +EventHandlerRegistry::HandlerState::~HandlerState()
|
| +{
|
| +}
|
| +
|
| +EventHandlerRegistry::EventHandlerRegistry(Document& document)
|
| + : m_document(document)
|
| + , m_documentObserver(DocumentObserver(document))
|
| + , m_windowObserver(adoptPtr(new WindowObserver(*this, *document.domWindow())))
|
| +{
|
| +}
|
| +
|
| +EventHandlerRegistry::~EventHandlerRegistry()
|
| +{
|
| +}
|
| +
|
| +const char* EventHandlerRegistry::supplementName()
|
| +{
|
| + return "EventHandlerRegistry";
|
| +}
|
| +
|
| +EventHandlerRegistry* EventHandlerRegistry::from(Document& document)
|
| +{
|
| + EventHandlerRegistry* registry = static_cast<EventHandlerRegistry*>(DocumentSupplement::from(document, supplementName()));
|
| + if (!registry) {
|
| + registry = new EventHandlerRegistry(document);
|
| + DocumentSupplement::provideTo(document, supplementName(), adoptPtr(registry));
|
| + }
|
| + return registry;
|
| +}
|
| +
|
| +bool EventHandlerRegistry::eventTypeToClass(const AtomicString& eventType, EventHandlerClass* result)
|
| +{
|
| + if (eventType == EventTypeNames::wheel || eventType == EventTypeNames::mousewheel) {
|
| + *result = WheelEvent;
|
| + } else if (eventType == EventTypeNames::scroll) {
|
| + *result = ScrollEvent;
|
| + } else if (isTouchEventType(eventType)) {
|
| + *result = TouchEvent;
|
| + } else {
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(EventHandlerClass handlerClass) const
|
| +{
|
| + return m_eventHandlers[handlerClass].targets.get();
|
| +}
|
| +
|
| +bool EventHandlerRegistry::hasEventHandlers(EventHandlerClass handlerClass) const
|
| +{
|
| + EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
|
| + return m_eventHandlers[handlerClass].externalHandlerCount || (targets && targets->size());
|
| +}
|
| +
|
| +unsigned EventHandlerRegistry::externalEventHandlerCount(EventHandlerClass handlerClass) const
|
| +{
|
| + return m_eventHandlers[handlerClass].externalHandlerCount;
|
| +}
|
| +
|
| +void EventHandlerRegistry::updateExternalHandlerCount(ChangeOperation op, EventHandlerClass handlerClass)
|
| +{
|
| + if (op == Add) {
|
| + m_eventHandlers[handlerClass].externalHandlerCount++;
|
| + } else {
|
| + ASSERT(op == Remove);
|
| + ASSERT(m_eventHandlers[handlerClass].externalHandlerCount > 0);
|
| + m_eventHandlers[handlerClass].externalHandlerCount--;
|
| + }
|
| +}
|
| +
|
| +bool EventHandlerRegistry::updateEventHandlerTargets(ChangeOperation op, EventHandlerClass handlerClass, EventTarget* target)
|
| +{
|
| + EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
|
| + if (op == Add) {
|
| +#ifndef NDEBUG
|
| + if (Node* node = target->toNode()) {
|
| + // The node should either be in the document, or be the Document node of a child
|
| + // of the document.
|
| + ASSERT(&node->document() == &m_document
|
| + || (node->isDocumentNode() && toDocument(node)->parentDocument() == &m_document));
|
| + } else if (DOMWindow* window = target->toDOMWindow()) {
|
| + // The window should belong to this document.
|
| + ASSERT(window->document() == &m_document);
|
| + }
|
| +#endif // NDEBUG
|
| +
|
| + if (!targets) {
|
| + m_eventHandlers[handlerClass].targets = adoptPtr(new EventTargetSet);
|
| + targets = m_eventHandlers[handlerClass].targets.get();
|
| + }
|
| +
|
| + if (!targets->add(target).isNewEntry) {
|
| + // Just incremented refcount, no real change.
|
| + // If this is a child document node, then the count should never go above 1.
|
| +#ifndef NDEBUG
|
| + if (Node* node = target->toNode())
|
| + ASSERT(!node->isDocumentNode() || &node->document() == &m_document);
|
| +#endif // NDEBUG
|
| + return false;
|
| + }
|
| + } else {
|
| + // Note that we can't assert that |target| is in this document because
|
| + // it might be in the process of moving out of it.
|
| + ASSERT(op == Remove || op == RemoveAll);
|
| + ASSERT(op == RemoveAll || targets->contains(target));
|
| + if (!targets)
|
| + return false;
|
| +
|
| + if (op == RemoveAll) {
|
| + if (!targets->contains(target))
|
| + return false;
|
| + targets->removeAll(target);
|
| + } else {
|
| + if (!targets->remove(target)) {
|
| + // Just decremented refcount, no real update.
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void EventHandlerRegistry::updateEventHandlerInternal(ChangeOperation op, EventHandlerClass handlerClass, EventTarget* target)
|
| +{
|
| + bool hadHandlers = hasEventHandlers(handlerClass);
|
| + bool targetSetChanged;
|
| + if (target) {
|
| + targetSetChanged = updateEventHandlerTargets(op, handlerClass, target);
|
| + } else {
|
| + updateExternalHandlerCount(op, handlerClass);
|
| + targetSetChanged = true;
|
| + }
|
| + bool hasHandlers = hasEventHandlers(handlerClass);
|
| +
|
| + // Notify the parent document's registry if we added the first or removed
|
| + // the last handler.
|
| + if (hadHandlers != hasHandlers) {
|
| + if (Document* parent = m_document.parentDocument()) {
|
| + // Report change to parent with our Document as the target.
|
| + EventHandlerRegistry::from(*parent)->updateEventHandlerInternal(op, handlerClass, &m_document);
|
| + } else {
|
| + // This is the root registry; notify clients accordingly.
|
| + notifyHasHandlersChanged(handlerClass, hasHandlers);
|
| + }
|
| + }
|
| +
|
| + // Only notify the client when something actually changed, and then only if
|
| + // this wasn't a notification that was sent by a sub-registry.
|
| + if (!targetSetChanged)
|
| + return;
|
| + Node* node = target ? target->toNode() : nullptr;
|
| + if (!node || !node->isDocumentNode() || node == &m_document)
|
| + notifyDidAddOrRemoveEventHandlerTarget(handlerClass);
|
| +}
|
| +
|
| +void EventHandlerRegistry::updateEventHandlerOfType(ChangeOperation op, const AtomicString& eventType, EventTarget* target)
|
| +{
|
| + EventHandlerClass handlerClass;
|
| + if (!eventTypeToClass(eventType, &handlerClass))
|
| + return;
|
| + updateEventHandlerInternal(op, handlerClass, target);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didAddExternalEventHandler(const AtomicString& eventType)
|
| +{
|
| + updateEventHandlerOfType(Add, eventType, nullptr);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didRemoveExternalEventHandler(const AtomicString& eventType)
|
| +{
|
| + updateEventHandlerOfType(Remove, eventType, nullptr);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didAddEventHandler(EventTarget& target, const AtomicString& eventType)
|
| +{
|
| + updateEventHandlerOfType(Add, eventType, &target);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, const AtomicString& eventType)
|
| +{
|
| + updateEventHandlerOfType(Remove, eventType, &target);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didAddEventHandler(EventTarget& target, EventHandlerClass handlerClass)
|
| +{
|
| + updateEventHandlerInternal(Add, handlerClass, &target);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, EventHandlerClass handlerClass)
|
| +{
|
| + updateEventHandlerInternal(Remove, handlerClass, &target);
|
| +}
|
| +
|
| +void EventHandlerRegistry::didMoveFromOtherDocument(EventTarget& target, Document& oldDocument)
|
| +{
|
| + EventHandlerRegistry* oldRegistry = EventHandlerRegistry::from(oldDocument);
|
| + for (size_t i = 0; i < EventHandlerClassCount; ++i) {
|
| + EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
|
| + const EventTargetSet* targets = oldRegistry->eventHandlerTargets(handlerClass);
|
| + if (!targets)
|
| + continue;
|
| + for (unsigned count = targets->count(&target); count > 0; --count) {
|
| + oldRegistry->updateEventHandlerInternal(Remove, handlerClass, &target);
|
| + updateEventHandlerInternal(Add, handlerClass, &target);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target)
|
| +{
|
| + for (size_t i = 0; i < EventHandlerClassCount; ++i) {
|
| + EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
|
| + const EventTargetSet* targets = eventHandlerTargets(handlerClass);
|
| + if (!targets)
|
| + continue;
|
| + updateEventHandlerInternal(RemoveAll, handlerClass, &target);
|
| + }
|
| +}
|
| +
|
| +void EventHandlerRegistry::notifyHasHandlersChanged(EventHandlerClass handlerClass, bool hasActiveHandlers)
|
| +{
|
| + Page* page = m_document.page();
|
| + LocalFrame* mainFrame = page ? page->mainFrame() : nullptr;
|
| + ScrollingCoordinator* scrollingCoordinator = page ? page->scrollingCoordinator() : nullptr;
|
| +
|
| + switch (handlerClass) {
|
| + case WheelEvent:
|
| + if (mainFrame) {
|
| + // TODO(skyostil): This notification is wired up to a black hole, so remove it.
|
| + mainFrame->notifyChromeClientWheelEventHandlerCountChanged();
|
| + }
|
| + if (scrollingCoordinator)
|
| + scrollingCoordinator->haveWheelEventHandlersChangedForPage();
|
| + break;
|
| + case ScrollEvent:
|
| + if (scrollingCoordinator)
|
| + scrollingCoordinator->haveScrollEventHandlersChangedForPage();
|
| + break;
|
| + case TouchEvent:
|
| + if (FrameHost* frameHost = m_document.frameHost())
|
| + frameHost->chrome().client().needTouchEvents(hasActiveHandlers);
|
| + break;
|
| + default:
|
| + ASSERT_NOT_REACHED();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void EventHandlerRegistry::notifyDidAddOrRemoveEventHandlerTarget(EventHandlerClass handlerClass)
|
| +{
|
| + Page* page = m_document.page();
|
| + if (!page)
|
| + return;
|
| +
|
| + ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator();
|
| + if (!scrollingCoordinator)
|
| + return;
|
| +
|
| + if (handlerClass == TouchEvent)
|
| + scrollingCoordinator->touchEventTargetRectsDidChange();
|
| +}
|
| +
|
| +} // namespace WebCore
|
|
|