OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 #include "config.h" |
| 5 #include "core/dom/EventHandlerRegistry.h" |
| 6 |
| 7 #include "core/dom/Document.h" |
| 8 #include "core/events/ThreadLocalEventNames.h" |
| 9 #include "core/events/WheelEvent.h" |
| 10 #include "core/frame/FrameHost.h" |
| 11 #include "core/frame/LocalFrame.h" |
| 12 #include "core/page/Chrome.h" |
| 13 #include "core/page/ChromeClient.h" |
| 14 #include "core/page/Page.h" |
| 15 #include "core/page/scrolling/ScrollingCoordinator.h" |
| 16 |
| 17 namespace WebCore { |
| 18 |
| 19 EventHandlerRegistry::WindowObserver::WindowObserver(EventHandlerRegistry& regis
try, DOMWindow& window) |
| 20 : DOMWindowLifecycleObserver(&window) |
| 21 , m_registry(registry) |
| 22 { |
| 23 } |
| 24 |
| 25 EventHandlerRegistry::WindowObserver::~WindowObserver() |
| 26 { |
| 27 } |
| 28 |
| 29 void EventHandlerRegistry::WindowObserver::didAddEventListener(DOMWindow* window
, const AtomicString& eventType) |
| 30 { |
| 31 m_registry.didAddEventHandler(*window, eventType); |
| 32 } |
| 33 |
| 34 void EventHandlerRegistry::WindowObserver::didRemoveEventListener(DOMWindow* win
dow, const AtomicString& eventType) |
| 35 { |
| 36 m_registry.didRemoveEventHandler(*window, eventType); |
| 37 } |
| 38 |
| 39 void EventHandlerRegistry::WindowObserver::didRemoveAllEventListeners(DOMWindow*
window) |
| 40 { |
| 41 m_registry.didRemoveAllEventHandlers(*window); |
| 42 } |
| 43 |
| 44 EventHandlerRegistry::DocumentObserver::DocumentObserver(Document& document) |
| 45 : DocumentLifecycleObserver(&document) { } |
| 46 |
| 47 EventHandlerRegistry::DocumentObserver::~DocumentObserver() |
| 48 { |
| 49 } |
| 50 |
| 51 void EventHandlerRegistry::DocumentObserver::documentWasAttached() |
| 52 { |
| 53 Document* document = lifecycleContext(); |
| 54 if (document->parentDocument()) |
| 55 return; |
| 56 EventHandlerRegistry* registry = EventHandlerRegistry::from(*document); |
| 57 |
| 58 // Make sure we are tracking handlers on the window. |
| 59 if (!registry->m_windowObserver && document->domWindow()) { |
| 60 ASSERT(!document->domWindow()->hasEventListeners()); |
| 61 registry->m_windowObserver = adoptPtr(new WindowObserver(*registry, *doc
ument->domWindow())); |
| 62 } |
| 63 |
| 64 // When the root document is attached, notify clients of any handlers it has
. |
| 65 for (size_t i = 0; i < EventHandlerClassCount; ++i) { |
| 66 EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); |
| 67 if (registry->hasEventHandlers(handlerClass)) { |
| 68 registry->notifyHasHandlersChanged(handlerClass, true); |
| 69 registry->notifyDidAddOrRemoveEventHandlerTarget(handlerClass); |
| 70 } |
| 71 } |
| 72 } |
| 73 |
| 74 void EventHandlerRegistry::DocumentObserver::willDetachDocument() |
| 75 { |
| 76 Document* document = lifecycleContext(); |
| 77 |
| 78 // The detached document should no longer track handlers on its window. |
| 79 EventHandlerRegistry* registry = EventHandlerRegistry::from(*document); |
| 80 if (document->domWindow()) |
| 81 registry->didRemoveAllEventHandlers(*document->domWindow()); |
| 82 registry->m_windowObserver.clear(); |
| 83 |
| 84 // Unregister our Document node from the parent for all event classes that |
| 85 // have handlers. |
| 86 Document* parentDocument = document->parentDocument(); |
| 87 if (!parentDocument) |
| 88 return; |
| 89 EventHandlerRegistry* parentRegistry = EventHandlerRegistry::from(*parentDoc
ument); |
| 90 parentRegistry->didRemoveAllEventHandlers(*document); |
| 91 } |
| 92 |
| 93 EventHandlerRegistry::HandlerState::HandlerState() |
| 94 : externalHandlerCount(0) |
| 95 { |
| 96 } |
| 97 |
| 98 EventHandlerRegistry::HandlerState::~HandlerState() |
| 99 { |
| 100 } |
| 101 |
| 102 EventHandlerRegistry::EventHandlerRegistry(Document& document) |
| 103 : m_document(document) |
| 104 , m_documentObserver(DocumentObserver(document)) |
| 105 , m_windowObserver(adoptPtr(new WindowObserver(*this, *document.domWindow())
)) |
| 106 { |
| 107 } |
| 108 |
| 109 EventHandlerRegistry::~EventHandlerRegistry() |
| 110 { |
| 111 } |
| 112 |
| 113 const char* EventHandlerRegistry::supplementName() |
| 114 { |
| 115 return "EventHandlerRegistry"; |
| 116 } |
| 117 |
| 118 EventHandlerRegistry* EventHandlerRegistry::from(Document& document) |
| 119 { |
| 120 EventHandlerRegistry* registry = static_cast<EventHandlerRegistry*>(Document
Supplement::from(document, supplementName())); |
| 121 if (!registry) { |
| 122 registry = new EventHandlerRegistry(document); |
| 123 DocumentSupplement::provideTo(document, supplementName(), adoptPtr(regis
try)); |
| 124 } |
| 125 return registry; |
| 126 } |
| 127 |
| 128 bool EventHandlerRegistry::eventTypeToClass(const AtomicString& eventType, Event
HandlerClass* result) |
| 129 { |
| 130 if (eventType == EventTypeNames::wheel || eventType == EventTypeNames::mouse
wheel) { |
| 131 *result = WheelEvent; |
| 132 } else if (eventType == EventTypeNames::scroll) { |
| 133 *result = ScrollEvent; |
| 134 } else if (isTouchEventType(eventType)) { |
| 135 *result = TouchEvent; |
| 136 } else { |
| 137 return false; |
| 138 } |
| 139 return true; |
| 140 } |
| 141 |
| 142 const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(EventHandlerClas
s handlerClass) const |
| 143 { |
| 144 return m_eventHandlers[handlerClass].targets.get(); |
| 145 } |
| 146 |
| 147 bool EventHandlerRegistry::hasEventHandlers(EventHandlerClass handlerClass) cons
t |
| 148 { |
| 149 EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get(); |
| 150 return m_eventHandlers[handlerClass].externalHandlerCount || (targets && tar
gets->size()); |
| 151 } |
| 152 |
| 153 unsigned EventHandlerRegistry::externalEventHandlerCount(EventHandlerClass handl
erClass) const |
| 154 { |
| 155 return m_eventHandlers[handlerClass].externalHandlerCount; |
| 156 } |
| 157 |
| 158 void EventHandlerRegistry::updateExternalHandlerCount(ChangeOperation op, EventH
andlerClass handlerClass) |
| 159 { |
| 160 if (op == Add) { |
| 161 m_eventHandlers[handlerClass].externalHandlerCount++; |
| 162 } else { |
| 163 ASSERT(op == Remove); |
| 164 ASSERT(m_eventHandlers[handlerClass].externalHandlerCount > 0); |
| 165 m_eventHandlers[handlerClass].externalHandlerCount--; |
| 166 } |
| 167 } |
| 168 |
| 169 bool EventHandlerRegistry::updateEventHandlerTargets(ChangeOperation op, EventHa
ndlerClass handlerClass, EventTarget* target) |
| 170 { |
| 171 EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get(); |
| 172 if (op == Add) { |
| 173 #ifndef NDEBUG |
| 174 if (Node* node = target->toNode()) { |
| 175 // The node should either be in the document, or be the Document nod
e of a child |
| 176 // of the document. |
| 177 ASSERT(&node->document() == &m_document |
| 178 || (node->isDocumentNode() && toDocument(node)->parentDocument()
== &m_document)); |
| 179 } else if (DOMWindow* window = target->toDOMWindow()) { |
| 180 // The window should belong to this document. |
| 181 ASSERT(window->document() == &m_document); |
| 182 } |
| 183 #endif // NDEBUG |
| 184 |
| 185 if (!targets) { |
| 186 m_eventHandlers[handlerClass].targets = adoptPtr(new EventTargetSet)
; |
| 187 targets = m_eventHandlers[handlerClass].targets.get(); |
| 188 } |
| 189 |
| 190 if (!targets->add(target).isNewEntry) { |
| 191 // Just incremented refcount, no real change. |
| 192 // If this is a child document node, then the count should never go
above 1. |
| 193 #ifndef NDEBUG |
| 194 if (Node* node = target->toNode()) |
| 195 ASSERT(!node->isDocumentNode() || &node->document() == &m_docume
nt); |
| 196 #endif // NDEBUG |
| 197 return false; |
| 198 } |
| 199 } else { |
| 200 // Note that we can't assert that |target| is in this document because |
| 201 // it might be in the process of moving out of it. |
| 202 ASSERT(op == Remove || op == RemoveAll); |
| 203 ASSERT(op == RemoveAll || targets->contains(target)); |
| 204 if (!targets) |
| 205 return false; |
| 206 |
| 207 if (op == RemoveAll) { |
| 208 if (!targets->contains(target)) |
| 209 return false; |
| 210 targets->removeAll(target); |
| 211 } else { |
| 212 if (!targets->remove(target)) { |
| 213 // Just decremented refcount, no real update. |
| 214 return false; |
| 215 } |
| 216 } |
| 217 } |
| 218 return true; |
| 219 } |
| 220 |
| 221 void EventHandlerRegistry::updateEventHandlerInternal(ChangeOperation op, EventH
andlerClass handlerClass, EventTarget* target) |
| 222 { |
| 223 bool hadHandlers = hasEventHandlers(handlerClass); |
| 224 bool targetSetChanged; |
| 225 if (target) { |
| 226 targetSetChanged = updateEventHandlerTargets(op, handlerClass, target); |
| 227 } else { |
| 228 updateExternalHandlerCount(op, handlerClass); |
| 229 targetSetChanged = true; |
| 230 } |
| 231 bool hasHandlers = hasEventHandlers(handlerClass); |
| 232 |
| 233 // Notify the parent document's registry if we added the first or removed |
| 234 // the last handler. |
| 235 if (hadHandlers != hasHandlers) { |
| 236 if (Document* parent = m_document.parentDocument()) { |
| 237 // Report change to parent with our Document as the target. |
| 238 EventHandlerRegistry::from(*parent)->updateEventHandlerInternal(op,
handlerClass, &m_document); |
| 239 } else { |
| 240 // This is the root registry; notify clients accordingly. |
| 241 notifyHasHandlersChanged(handlerClass, hasHandlers); |
| 242 } |
| 243 } |
| 244 |
| 245 // Only notify the client when something actually changed, and then only if |
| 246 // this wasn't a notification that was sent by a sub-registry. |
| 247 if (!targetSetChanged) |
| 248 return; |
| 249 Node* node = target ? target->toNode() : nullptr; |
| 250 if (!node || !node->isDocumentNode() || node == &m_document) |
| 251 notifyDidAddOrRemoveEventHandlerTarget(handlerClass); |
| 252 } |
| 253 |
| 254 void EventHandlerRegistry::updateEventHandlerOfType(ChangeOperation op, const At
omicString& eventType, EventTarget* target) |
| 255 { |
| 256 EventHandlerClass handlerClass; |
| 257 if (!eventTypeToClass(eventType, &handlerClass)) |
| 258 return; |
| 259 updateEventHandlerInternal(op, handlerClass, target); |
| 260 } |
| 261 |
| 262 void EventHandlerRegistry::didAddExternalEventHandler(const AtomicString& eventT
ype) |
| 263 { |
| 264 updateEventHandlerOfType(Add, eventType, nullptr); |
| 265 } |
| 266 |
| 267 void EventHandlerRegistry::didRemoveExternalEventHandler(const AtomicString& eve
ntType) |
| 268 { |
| 269 updateEventHandlerOfType(Remove, eventType, nullptr); |
| 270 } |
| 271 |
| 272 void EventHandlerRegistry::didAddEventHandler(EventTarget& target, const AtomicS
tring& eventType) |
| 273 { |
| 274 updateEventHandlerOfType(Add, eventType, &target); |
| 275 } |
| 276 |
| 277 void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, const Atom
icString& eventType) |
| 278 { |
| 279 updateEventHandlerOfType(Remove, eventType, &target); |
| 280 } |
| 281 |
| 282 void EventHandlerRegistry::didAddEventHandler(EventTarget& target, EventHandlerC
lass handlerClass) |
| 283 { |
| 284 updateEventHandlerInternal(Add, handlerClass, &target); |
| 285 } |
| 286 |
| 287 void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, EventHandl
erClass handlerClass) |
| 288 { |
| 289 updateEventHandlerInternal(Remove, handlerClass, &target); |
| 290 } |
| 291 |
| 292 void EventHandlerRegistry::didMoveFromOtherDocument(EventTarget& target, Documen
t& oldDocument) |
| 293 { |
| 294 EventHandlerRegistry* oldRegistry = EventHandlerRegistry::from(oldDocument); |
| 295 for (size_t i = 0; i < EventHandlerClassCount; ++i) { |
| 296 EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); |
| 297 const EventTargetSet* targets = oldRegistry->eventHandlerTargets(handler
Class); |
| 298 if (!targets) |
| 299 continue; |
| 300 for (unsigned count = targets->count(&target); count > 0; --count) { |
| 301 oldRegistry->updateEventHandlerInternal(Remove, handlerClass, &targe
t); |
| 302 updateEventHandlerInternal(Add, handlerClass, &target); |
| 303 } |
| 304 } |
| 305 } |
| 306 |
| 307 void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target) |
| 308 { |
| 309 for (size_t i = 0; i < EventHandlerClassCount; ++i) { |
| 310 EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); |
| 311 const EventTargetSet* targets = eventHandlerTargets(handlerClass); |
| 312 if (!targets) |
| 313 continue; |
| 314 updateEventHandlerInternal(RemoveAll, handlerClass, &target); |
| 315 } |
| 316 } |
| 317 |
| 318 void EventHandlerRegistry::notifyHasHandlersChanged(EventHandlerClass handlerCla
ss, bool hasActiveHandlers) |
| 319 { |
| 320 Page* page = m_document.page(); |
| 321 LocalFrame* mainFrame = page ? page->mainFrame() : nullptr; |
| 322 ScrollingCoordinator* scrollingCoordinator = page ? page->scrollingCoordinat
or() : nullptr; |
| 323 |
| 324 switch (handlerClass) { |
| 325 case WheelEvent: |
| 326 if (mainFrame) { |
| 327 // TODO(skyostil): This notification is wired up to a black hole, so
remove it. |
| 328 mainFrame->notifyChromeClientWheelEventHandlerCountChanged(); |
| 329 } |
| 330 if (scrollingCoordinator) |
| 331 scrollingCoordinator->haveWheelEventHandlersChangedForPage(); |
| 332 break; |
| 333 case ScrollEvent: |
| 334 if (scrollingCoordinator) |
| 335 scrollingCoordinator->haveScrollEventHandlersChangedForPage(); |
| 336 break; |
| 337 case TouchEvent: |
| 338 if (FrameHost* frameHost = m_document.frameHost()) |
| 339 frameHost->chrome().client().needTouchEvents(hasActiveHandlers); |
| 340 break; |
| 341 default: |
| 342 ASSERT_NOT_REACHED(); |
| 343 break; |
| 344 } |
| 345 } |
| 346 |
| 347 void EventHandlerRegistry::notifyDidAddOrRemoveEventHandlerTarget(EventHandlerCl
ass handlerClass) |
| 348 { |
| 349 Page* page = m_document.page(); |
| 350 if (!page) |
| 351 return; |
| 352 |
| 353 ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator(); |
| 354 if (!scrollingCoordinator) |
| 355 return; |
| 356 |
| 357 if (handlerClass == TouchEvent) |
| 358 scrollingCoordinator->touchEventTargetRectsDidChange(); |
| 359 } |
| 360 |
| 361 } // namespace WebCore |
OLD | NEW |