Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Unified Diff: Source/core/dom/EventHandlerRegistry.cpp

Issue 206603002: Add EventHandlerRegistry (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Tests now seem to pass. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..ac04f991991cab82b9d623298f5d74727ab826bb
--- /dev/null
+++ b/Source/core/dom/EventHandlerRegistry.cpp
@@ -0,0 +1,343 @@
+// 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::HandlerState::HandlerState()
+ : handlerCount(0)
+ , externalHandlerCount(0)
+{
+}
+
+EventHandlerRegistry::HandlerState::~HandlerState()
+{
+}
+
+EventHandlerRegistry::EventHandlerRegistry(Document& document)
+ : DOMWindowLifecycleObserver(document.domWindow())
+ , DocumentLifecycleObserver(&document)
+{
+}
+
+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;
+}
+
+unsigned EventHandlerRegistry::eventHandlerCount(EventHandlerClass handlerClass) const
+{
+ return m_eventHandlers[handlerClass].handlerCount + m_eventHandlers[handlerClass].externalHandlerCount;
+}
+
+const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(EventHandlerClass handlerClass) const
+{
+ return m_eventHandlers[handlerClass].targets.get();
+}
+
+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, Document& document, EventTarget* target)
+{
+ EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
+ if (op == Add) {
+ if (Node* node = target->toNode()) {
+ // The node should either be in the document, or be the Document node of a child
Rick Byers 2014/03/27 16:43:31 I like this assert, but again, but what if 'docume
Sami 2014/04/02 19:58:05 Agreed. This is now cleared up too.
+ // of the document.
+ ASSERT(&node->document() == &document
+ || (node->isDocumentNode() && toDocument(node)->parentDocument() == &document));
+ } else if (DOMWindow* window = target->toDOMWindow()) {
+ // The window should belong to this document.
+ ASSERT(window->document() == &document);
+ }
+
+ if (!targets) {
+ m_eventHandlers[handlerClass].targets = adoptPtr(new EventTargetSet);
+ targets = m_eventHandlers[handlerClass].targets.get();
+ }
+
+ m_eventHandlers[handlerClass].handlerCount++;
+ 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.
+ if (Node* node = target->toNode())
+ ASSERT(!node->isDocumentNode() || &node->document() == &document);
+ 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;
+ m_eventHandlers[handlerClass].handlerCount -= targets->count(target);
+ targets->removeAll(target);
+ } else {
+ m_eventHandlers[handlerClass].handlerCount--;
+ if (!targets->remove(target)) {
+ // Just decremented refcount, no real update.
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void EventHandlerRegistry::checkEventHandlerConsistency(EventHandlerClass handlerClass) const
+{
+#ifndef NDEBUG
+ EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
+ if (!targets) {
+ ASSERT(!m_eventHandlers[handlerClass].handlerCount);
+ return;
+ }
+
+ unsigned count = 0;
+ for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter)
+ count += iter->value;
+ ASSERT(count == m_eventHandlers[handlerClass].handlerCount);
+#endif // NDEBUG
+}
+
+void EventHandlerRegistry::updateEventHandlerInternal(ChangeOperation op, Document& document, EventHandlerClass handlerClass, EventTarget* target)
+{
+ bool hadHandlers = eventHandlerCount(handlerClass);
+ bool targetSetChanged;
+ if (target) {
+ targetSetChanged = updateEventHandlerTargets(op, handlerClass, document, target);
+ checkEventHandlerConsistency(handlerClass);
+ } else {
+ updateExternalHandlerCount(op, handlerClass);
+ targetSetChanged = true;
+ }
+ bool haveHandlers = eventHandlerCount(handlerClass);
+
+ // Notify the parent document's registry if we added the first or removed
+ // the last handler.
+ bool addedFirstHandler = !hadHandlers && haveHandlers;
+ bool removedLastHandler = hadHandlers && !haveHandlers;
+ if (addedFirstHandler || removedLastHandler) {
Rick Byers 2014/03/27 16:43:31 simpler just to check if(hadHandlers != haveHandle
Sami 2014/04/02 19:58:05 D'oh, done.
+ if (Document* parent = document.parentDocument()) {
+ // Report change to parent with our Document as the target.
+ EventHandlerRegistry::from(*parent)->updateEventHandlerInternal(op, *parent, handlerClass, &document);
Rick Byers 2014/03/27 16:43:31 It's calls like this that make me think the Docume
Sami 2014/04/02 19:58:05 Done.
+ } else {
+ // This is the root registry; notify clients accordingly.
+ notifyDidAddFirstOrRemoveLastEventHandler(document, handlerClass, haveHandlers);
+ }
+ }
+
+ // 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 == &document)
+ notifyDidAddOrRemoveEventHandlerTarget(handlerClass, document);
+}
+
+void EventHandlerRegistry::updateEventHandlerOfClass(ChangeOperation op, EventHandlerClass handlerClass, EventTarget& target)
+{
Rick Byers 2014/03/27 16:43:31 this function should collapse to nothing if you ca
Sami 2014/04/02 19:58:05 Done.
+ if (Node* node = target.toNode())
+ updateEventHandlerInternal(op, node->document(), handlerClass, node);
+ else if (DOMWindow* window = target.toDOMWindow())
+ updateEventHandlerInternal(op, *window->document(), handlerClass, window);
+ else
+ ASSERT_NOT_REACHED();
+}
+
+void EventHandlerRegistry::updateEventHandlerOfType(ChangeOperation op, const AtomicString& eventType, EventTarget& target)
+{
+ EventHandlerClass handlerClass;
+ if (!eventTypeToClass(eventType, &handlerClass))
+ return;
+ updateEventHandlerOfClass(op, handlerClass, target);
+}
+
+void EventHandlerRegistry::didAddExternalEventHandler(Document& document, const AtomicString& eventType)
+{
+ EventHandlerClass handlerClass;
+ if (!eventTypeToClass(eventType, &handlerClass))
+ return;
+ updateEventHandlerInternal(Add, document, handlerClass, nullptr);
+}
+
+void EventHandlerRegistry::didRemoveExternalEventHandler(Document& document, const AtomicString& eventType)
+{
+ EventHandlerClass handlerClass;
+ if (!eventTypeToClass(eventType, &handlerClass))
+ return;
+ updateEventHandlerInternal(Remove, document, handlerClass, nullptr);
+}
+
+void EventHandlerRegistry::willDetachDocument()
+{
+ Document* document = DocumentLifecycleObserver::lifecycleContext();
+ Document* parentDocument = document->parentDocument();
+ if (!parentDocument)
+ return;
+ // Unregister our Document node from the parent for all event classes that
+ // have handlers.
+ EventHandlerRegistry* parentRegistry = EventHandlerRegistry::from(*parentDocument);
+ updateAllEventHandlersForTarget(RemoveAll, *document, *parentRegistry);
+}
+
+void EventHandlerRegistry::documentWasAttached()
+{
+ Document* document = DocumentLifecycleObserver::lifecycleContext();
+ if (document->parentDocument())
+ return;
+ for (size_t i = 0; i < EventHandlerClassCount; ++i) {
+ EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
+ if (eventHandlerCount(handlerClass))
+ notifyDidAddFirstOrRemoveLastEventHandler(*document, handlerClass, true);
+ }
+}
+
+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::didAddEventListener(DOMWindow* window, const AtomicString& eventType)
+{
+ updateEventHandlerOfType(Add, eventType, *window);
+}
+
+void EventHandlerRegistry::didRemoveEventListener(DOMWindow* window, const AtomicString& eventType)
+{
+ updateEventHandlerOfType(Remove, eventType, *window);
+}
+
+void EventHandlerRegistry::didRemoveAllEventListeners(DOMWindow* window)
+{
+ updateAllEventHandlersForTarget(RemoveAll, *window, *this);
+}
+
+void EventHandlerRegistry::didMoveToNewDocument(EventTarget& target, Document& oldDocument)
+{
+ EventHandlerRegistry* oldRegistry = EventHandlerRegistry::from(oldDocument);
+ oldRegistry->updateAllEventHandlersForTarget(Add, target, *this);
+ oldRegistry->updateAllEventHandlersForTarget(Remove, target, *oldRegistry);
+}
+
+void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target)
+{
+ updateAllEventHandlersForTarget(RemoveAll, target, *this);
+}
+
+void EventHandlerRegistry::updateAllEventHandlersForTarget(ChangeOperation op, EventTarget& target, EventHandlerRegistry& targetRegistry) const
Rick Byers 2014/03/27 16:43:31 I find this function confusing. Perhaps there's r
Sami 2014/04/02 19:58:05 Right, I tried a little too hard to reuse code and
+{
+ for (size_t i = 0; i < EventHandlerClassCount; ++i) {
+ EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
+ const EventTargetSet* targets = eventHandlerTargets(handlerClass);
+ if (!targets)
+ continue;
+ for (unsigned count = targets->count(&target); count > 0; --count)
Rick Byers 2014/03/27 16:43:31 This loop doesn't seem to make sense when the op i
Sami 2014/04/02 19:58:05 Done.
+ targetRegistry.updateEventHandlerOfClass(op, handlerClass, target);
+ }
+}
+
+void EventHandlerRegistry::notifyDidAddFirstOrRemoveLastEventHandler(Document& document, EventHandlerClass handlerClass, bool hasActiveHandlers)
+{
+ Page* page = document.page();
+ LocalFrame* mainFrame = page ? page->mainFrame() : nullptr;
+ ScrollingCoordinator* scrollingCoordinator = page ? page->scrollingCoordinator() : nullptr;
+ FrameView* frameView = document.view();
+
+ switch (handlerClass) {
+ case WheelEvent:
+ if (mainFrame) {
+ // TODO(skyostil): This notification is wired up to a black hole, so remove it.
+ mainFrame->notifyChromeClientWheelEventHandlerCountChanged();
+ }
+ if (scrollingCoordinator && frameView)
+ scrollingCoordinator->frameViewWheelEventHandlerCountChanged(frameView);
Rick Byers 2014/03/27 16:43:31 This function and the below say they're supposed t
Sami 2014/04/02 19:58:05 Yes, especially now that ScrollingCoordinator no l
+ break;
+ case ScrollEvent:
+ if (scrollingCoordinator && frameView)
+ scrollingCoordinator->frameViewScrollEventHandlerCountChanged(frameView);
+ break;
+ case TouchEvent:
+ if (FrameHost* frameHost = document.frameHost())
+ frameHost->chrome().client().needTouchEvents(hasActiveHandlers);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
+void EventHandlerRegistry::notifyDidAddOrRemoveEventHandlerTarget(EventHandlerClass handlerClass, Document& document)
+{
+ Page* page = document.page();
+ if (!page)
+ return;
+
+ ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator();
+ if (!scrollingCoordinator)
+ return;
+
+ if (handlerClass == TouchEvent)
+ scrollingCoordinator->touchEventTargetRectsDidChange();
+}
+
+} // namespace WebCore

Powered by Google App Engine
This is Rietveld 408576698