Chromium Code Reviews| 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::HandlerState::HandlerState() | |
| 20 : handlerCount(0) | |
| 21 , externalHandlerCount(0) | |
| 22 { | |
| 23 } | |
| 24 | |
| 25 EventHandlerRegistry::HandlerState::~HandlerState() | |
| 26 { | |
| 27 } | |
| 28 | |
| 29 EventHandlerRegistry::EventHandlerRegistry(Document& document) | |
| 30 : DOMWindowLifecycleObserver(document.domWindow()) | |
| 31 , DocumentLifecycleObserver(&document) | |
| 32 { | |
| 33 } | |
| 34 | |
| 35 EventHandlerRegistry::~EventHandlerRegistry() | |
| 36 { | |
| 37 } | |
| 38 | |
| 39 const char* EventHandlerRegistry::supplementName() | |
| 40 { | |
| 41 return "EventHandlerRegistry"; | |
| 42 } | |
| 43 | |
| 44 EventHandlerRegistry* EventHandlerRegistry::from(Document& document) | |
| 45 { | |
| 46 EventHandlerRegistry* registry = static_cast<EventHandlerRegistry*>(Document Supplement::from(document, supplementName())); | |
| 47 if (!registry) { | |
| 48 registry = new EventHandlerRegistry(document); | |
| 49 DocumentSupplement::provideTo(document, supplementName(), adoptPtr(regis try)); | |
| 50 } | |
| 51 return registry; | |
| 52 } | |
| 53 | |
| 54 bool EventHandlerRegistry::eventTypeToClass(const AtomicString& eventType, Event HandlerClass* result) | |
| 55 { | |
| 56 if (eventType == EventTypeNames::wheel || eventType == EventTypeNames::mouse wheel) { | |
| 57 *result = WheelEvent; | |
| 58 } else if (eventType == EventTypeNames::scroll) { | |
| 59 *result = ScrollEvent; | |
| 60 } else if (isTouchEventType(eventType)) { | |
| 61 *result = TouchEvent; | |
| 62 } else { | |
| 63 return false; | |
| 64 } | |
| 65 return true; | |
| 66 } | |
| 67 | |
| 68 unsigned EventHandlerRegistry::eventHandlerCount(EventHandlerClass handlerClass) const | |
| 69 { | |
| 70 return m_eventHandlers[handlerClass].handlerCount + m_eventHandlers[handlerC lass].externalHandlerCount; | |
| 71 } | |
| 72 | |
| 73 const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(EventHandlerClas s handlerClass) const | |
| 74 { | |
| 75 return m_eventHandlers[handlerClass].targets.get(); | |
| 76 } | |
| 77 | |
| 78 void EventHandlerRegistry::updateExternalHandlerCount(ChangeOperation op, EventH andlerClass handlerClass) | |
| 79 { | |
| 80 if (op == Add) { | |
| 81 m_eventHandlers[handlerClass].externalHandlerCount++; | |
| 82 } else { | |
| 83 ASSERT(op == Remove); | |
| 84 ASSERT(m_eventHandlers[handlerClass].externalHandlerCount > 0); | |
| 85 m_eventHandlers[handlerClass].externalHandlerCount--; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 bool EventHandlerRegistry::updateEventHandlerTargets(ChangeOperation op, EventHa ndlerClass handlerClass, Document& document, EventTarget* target) | |
| 90 { | |
| 91 EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get(); | |
| 92 if (op == Add) { | |
| 93 if (Node* node = target->toNode()) { | |
| 94 // The node should either be in the document, or be the Document nod e 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.
| |
| 95 // of the document. | |
| 96 ASSERT(&node->document() == &document | |
| 97 || (node->isDocumentNode() && toDocument(node)->parentDocument() == &document)); | |
| 98 } else if (DOMWindow* window = target->toDOMWindow()) { | |
| 99 // The window should belong to this document. | |
| 100 ASSERT(window->document() == &document); | |
| 101 } | |
| 102 | |
| 103 if (!targets) { | |
| 104 m_eventHandlers[handlerClass].targets = adoptPtr(new EventTargetSet) ; | |
| 105 targets = m_eventHandlers[handlerClass].targets.get(); | |
| 106 } | |
| 107 | |
| 108 m_eventHandlers[handlerClass].handlerCount++; | |
| 109 if (!targets->add(target).isNewEntry) { | |
| 110 // Just incremented refcount, no real change. | |
| 111 // If this is a child document node, then the count should never go above 1. | |
| 112 if (Node* node = target->toNode()) | |
| 113 ASSERT(!node->isDocumentNode() || &node->document() == &document ); | |
| 114 return false; | |
| 115 } | |
| 116 } else { | |
| 117 // Note that we can't assert that |target| is in this document because | |
| 118 // it might be in the process of moving out of it. | |
| 119 ASSERT(op == Remove || op == RemoveAll); | |
| 120 ASSERT(op == RemoveAll || targets->contains(target)); | |
| 121 if (!targets) | |
| 122 return false; | |
| 123 | |
| 124 if (op == RemoveAll) { | |
| 125 if (!targets->contains(target)) | |
| 126 return false; | |
| 127 m_eventHandlers[handlerClass].handlerCount -= targets->count(target) ; | |
| 128 targets->removeAll(target); | |
| 129 } else { | |
| 130 m_eventHandlers[handlerClass].handlerCount--; | |
| 131 if (!targets->remove(target)) { | |
| 132 // Just decremented refcount, no real update. | |
| 133 return false; | |
| 134 } | |
| 135 } | |
| 136 } | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 void EventHandlerRegistry::checkEventHandlerConsistency(EventHandlerClass handle rClass) const | |
| 141 { | |
| 142 #ifndef NDEBUG | |
| 143 EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get(); | |
| 144 if (!targets) { | |
| 145 ASSERT(!m_eventHandlers[handlerClass].handlerCount); | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 unsigned count = 0; | |
| 150 for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets ->end(); ++iter) | |
| 151 count += iter->value; | |
| 152 ASSERT(count == m_eventHandlers[handlerClass].handlerCount); | |
| 153 #endif // NDEBUG | |
| 154 } | |
| 155 | |
| 156 void EventHandlerRegistry::updateEventHandlerInternal(ChangeOperation op, Docume nt& document, EventHandlerClass handlerClass, EventTarget* target) | |
| 157 { | |
| 158 bool hadHandlers = eventHandlerCount(handlerClass); | |
| 159 bool targetSetChanged; | |
| 160 if (target) { | |
| 161 targetSetChanged = updateEventHandlerTargets(op, handlerClass, document, target); | |
| 162 checkEventHandlerConsistency(handlerClass); | |
| 163 } else { | |
| 164 updateExternalHandlerCount(op, handlerClass); | |
| 165 targetSetChanged = true; | |
| 166 } | |
| 167 bool haveHandlers = eventHandlerCount(handlerClass); | |
| 168 | |
| 169 // Notify the parent document's registry if we added the first or removed | |
| 170 // the last handler. | |
| 171 bool addedFirstHandler = !hadHandlers && haveHandlers; | |
| 172 bool removedLastHandler = hadHandlers && !haveHandlers; | |
| 173 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.
| |
| 174 if (Document* parent = document.parentDocument()) { | |
| 175 // Report change to parent with our Document as the target. | |
| 176 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.
| |
| 177 } else { | |
| 178 // This is the root registry; notify clients accordingly. | |
| 179 notifyDidAddFirstOrRemoveLastEventHandler(document, handlerClass, ha veHandlers); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 // Only notify the client when something actually changed, and then only if | |
| 184 // this wasn't a notification that was sent by a sub-registry. | |
| 185 if (!targetSetChanged) | |
| 186 return; | |
| 187 Node* node = target ? target->toNode() : nullptr; | |
| 188 if (!node || !node->isDocumentNode() || node == &document) | |
| 189 notifyDidAddOrRemoveEventHandlerTarget(handlerClass, document); | |
| 190 } | |
| 191 | |
| 192 void EventHandlerRegistry::updateEventHandlerOfClass(ChangeOperation op, EventHa ndlerClass handlerClass, EventTarget& target) | |
| 193 { | |
|
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.
| |
| 194 if (Node* node = target.toNode()) | |
| 195 updateEventHandlerInternal(op, node->document(), handlerClass, node); | |
| 196 else if (DOMWindow* window = target.toDOMWindow()) | |
| 197 updateEventHandlerInternal(op, *window->document(), handlerClass, window ); | |
| 198 else | |
| 199 ASSERT_NOT_REACHED(); | |
| 200 } | |
| 201 | |
| 202 void EventHandlerRegistry::updateEventHandlerOfType(ChangeOperation op, const At omicString& eventType, EventTarget& target) | |
| 203 { | |
| 204 EventHandlerClass handlerClass; | |
| 205 if (!eventTypeToClass(eventType, &handlerClass)) | |
| 206 return; | |
| 207 updateEventHandlerOfClass(op, handlerClass, target); | |
| 208 } | |
| 209 | |
| 210 void EventHandlerRegistry::didAddExternalEventHandler(Document& document, const AtomicString& eventType) | |
| 211 { | |
| 212 EventHandlerClass handlerClass; | |
| 213 if (!eventTypeToClass(eventType, &handlerClass)) | |
| 214 return; | |
| 215 updateEventHandlerInternal(Add, document, handlerClass, nullptr); | |
| 216 } | |
| 217 | |
| 218 void EventHandlerRegistry::didRemoveExternalEventHandler(Document& document, con st AtomicString& eventType) | |
| 219 { | |
| 220 EventHandlerClass handlerClass; | |
| 221 if (!eventTypeToClass(eventType, &handlerClass)) | |
| 222 return; | |
| 223 updateEventHandlerInternal(Remove, document, handlerClass, nullptr); | |
| 224 } | |
| 225 | |
| 226 void EventHandlerRegistry::willDetachDocument() | |
| 227 { | |
| 228 Document* document = DocumentLifecycleObserver::lifecycleContext(); | |
| 229 Document* parentDocument = document->parentDocument(); | |
| 230 if (!parentDocument) | |
| 231 return; | |
| 232 // Unregister our Document node from the parent for all event classes that | |
| 233 // have handlers. | |
| 234 EventHandlerRegistry* parentRegistry = EventHandlerRegistry::from(*parentDoc ument); | |
| 235 updateAllEventHandlersForTarget(RemoveAll, *document, *parentRegistry); | |
| 236 } | |
| 237 | |
| 238 void EventHandlerRegistry::documentWasAttached() | |
| 239 { | |
| 240 Document* document = DocumentLifecycleObserver::lifecycleContext(); | |
| 241 if (document->parentDocument()) | |
| 242 return; | |
| 243 for (size_t i = 0; i < EventHandlerClassCount; ++i) { | |
| 244 EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); | |
| 245 if (eventHandlerCount(handlerClass)) | |
| 246 notifyDidAddFirstOrRemoveLastEventHandler(*document, handlerClass, t rue); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 void EventHandlerRegistry::didAddEventHandler(EventTarget& target, const AtomicS tring& eventType) | |
| 251 { | |
| 252 updateEventHandlerOfType(Add, eventType, target); | |
| 253 } | |
| 254 | |
| 255 void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, const Atom icString& eventType) | |
| 256 { | |
| 257 updateEventHandlerOfType(Remove, eventType, target); | |
| 258 } | |
| 259 | |
| 260 void EventHandlerRegistry::didAddEventListener(DOMWindow* window, const AtomicSt ring& eventType) | |
| 261 { | |
| 262 updateEventHandlerOfType(Add, eventType, *window); | |
| 263 } | |
| 264 | |
| 265 void EventHandlerRegistry::didRemoveEventListener(DOMWindow* window, const Atomi cString& eventType) | |
| 266 { | |
| 267 updateEventHandlerOfType(Remove, eventType, *window); | |
| 268 } | |
| 269 | |
| 270 void EventHandlerRegistry::didRemoveAllEventListeners(DOMWindow* window) | |
| 271 { | |
| 272 updateAllEventHandlersForTarget(RemoveAll, *window, *this); | |
| 273 } | |
| 274 | |
| 275 void EventHandlerRegistry::didMoveToNewDocument(EventTarget& target, Document& o ldDocument) | |
| 276 { | |
| 277 EventHandlerRegistry* oldRegistry = EventHandlerRegistry::from(oldDocument); | |
| 278 oldRegistry->updateAllEventHandlersForTarget(Add, target, *this); | |
| 279 oldRegistry->updateAllEventHandlersForTarget(Remove, target, *oldRegistry); | |
| 280 } | |
| 281 | |
| 282 void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target) | |
| 283 { | |
| 284 updateAllEventHandlersForTarget(RemoveAll, target, *this); | |
| 285 } | |
| 286 | |
| 287 void EventHandlerRegistry::updateAllEventHandlersForTarget(ChangeOperation op, E ventTarget& 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
| |
| 288 { | |
| 289 for (size_t i = 0; i < EventHandlerClassCount; ++i) { | |
| 290 EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); | |
| 291 const EventTargetSet* targets = eventHandlerTargets(handlerClass); | |
| 292 if (!targets) | |
| 293 continue; | |
| 294 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.
| |
| 295 targetRegistry.updateEventHandlerOfClass(op, handlerClass, target); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 void EventHandlerRegistry::notifyDidAddFirstOrRemoveLastEventHandler(Document& d ocument, EventHandlerClass handlerClass, bool hasActiveHandlers) | |
| 300 { | |
| 301 Page* page = document.page(); | |
| 302 LocalFrame* mainFrame = page ? page->mainFrame() : nullptr; | |
| 303 ScrollingCoordinator* scrollingCoordinator = page ? page->scrollingCoordinat or() : nullptr; | |
| 304 FrameView* frameView = document.view(); | |
| 305 | |
| 306 switch (handlerClass) { | |
| 307 case WheelEvent: | |
| 308 if (mainFrame) { | |
| 309 // TODO(skyostil): This notification is wired up to a black hole, so remove it. | |
| 310 mainFrame->notifyChromeClientWheelEventHandlerCountChanged(); | |
| 311 } | |
| 312 if (scrollingCoordinator && frameView) | |
| 313 scrollingCoordinator->frameViewWheelEventHandlerCountChanged(frameVi ew); | |
|
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
| |
| 314 break; | |
| 315 case ScrollEvent: | |
| 316 if (scrollingCoordinator && frameView) | |
| 317 scrollingCoordinator->frameViewScrollEventHandlerCountChanged(frameV iew); | |
| 318 break; | |
| 319 case TouchEvent: | |
| 320 if (FrameHost* frameHost = document.frameHost()) | |
| 321 frameHost->chrome().client().needTouchEvents(hasActiveHandlers); | |
| 322 break; | |
| 323 default: | |
| 324 ASSERT_NOT_REACHED(); | |
| 325 break; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 void EventHandlerRegistry::notifyDidAddOrRemoveEventHandlerTarget(EventHandlerCl ass handlerClass, Document& document) | |
| 330 { | |
| 331 Page* page = document.page(); | |
| 332 if (!page) | |
| 333 return; | |
| 334 | |
| 335 ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator(); | |
| 336 if (!scrollingCoordinator) | |
| 337 return; | |
| 338 | |
| 339 if (handlerClass == TouchEvent) | |
| 340 scrollingCoordinator->touchEventTargetRectsDidChange(); | |
| 341 } | |
| 342 | |
| 343 } // namespace WebCore | |
| OLD | NEW |