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