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

Side by Side Diff: third_party/WebKit/Source/core/input/PointerEventManager.cpp

Issue 1904453003: Change finding common ancestor from O(n^2) to O(n) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Applying the comments Created 4 years, 8 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/input/PointerEventManager.h" 5 #include "core/input/PointerEventManager.h"
6 6
7 #include "core/dom/ElementTraversal.h" 7 #include "core/dom/ElementTraversal.h"
8 #include "core/dom/shadow/FlatTreeTraversal.h" 8 #include "core/dom/shadow/FlatTreeTraversal.h"
9 #include "core/events/MouseEvent.h" 9 #include "core/events/MouseEvent.h"
10 #include "core/html/HTMLCanvasElement.h" 10 #include "core/html/HTMLCanvasElement.h"
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 HTMLCanvasElement* canvas = Traversal<HTMLCanvasElement>::firstAncestorOrSel f(*element); 72 HTMLCanvasElement* canvas = Traversal<HTMLCanvasElement>::firstAncestorOrSel f(*element);
73 // In this case, the event target is canvas and mouse rerouting doesn't happ en. 73 // In this case, the event target is canvas and mouse rerouting doesn't happ en.
74 if (canvas == element) 74 if (canvas == element)
75 return mouseEvent; 75 return mouseEvent;
76 String region = canvas->getIdFromControl(element); 76 String region = canvas->getIdFromControl(element);
77 PlatformMouseEvent newMouseEvent = mouseEvent; 77 PlatformMouseEvent newMouseEvent = mouseEvent;
78 newMouseEvent.setRegion(region); 78 newMouseEvent.setRegion(region);
79 return newMouseEvent; 79 return newMouseEvent;
80 } 80 }
81 81
82 void buildAncestorChainsAndFindCommonAncestors(
83 EventTarget* exitedTarget, EventTarget* enteredTarget,
bokan 2016/04/21 21:38:29 Seems like these can't be null? If that's true, ma
Navid Zolghadr 2016/04/21 23:40:14 No. They can be null. The isInDocument checks for
bokan 2016/04/22 00:52:54 Got it, ok, fine as they are (if possible though,
Navid Zolghadr 2016/04/22 17:04:12 This turned out to be harder than I thought. Marki
bokan 2016/04/22 17:30:52 Nah, if it's more trouble lgtm as-is. Our code is
84 HeapVector<Member<Node>, 20> &exitedAncestors,
85 HeapVector<Member<Node>, 20> &enteredAncestors,
86 size_t& exitedAncestorsCommonParentIndex,
87 size_t& enteredAncestorsCommonParentIndex)
bokan 2016/04/21 21:38:28 Output parameters should be passed by pointer.
Navid Zolghadr 2016/04/21 23:40:14 You mean like size_t* and HeapVector<Member<Node>
bokan 2016/04/22 00:52:54 Yup, see the bit about output params here: https:/
Navid Zolghadr 2016/04/22 17:04:12 Done.
88 {
89 // Index 0 element in the ancestors arrays will be the corresponding
90 // target. So the root of their document will be their last element.
91
bokan 2016/04/21 21:38:29 I see you check if exitedTarget == enteredTarget b
Navid Zolghadr 2016/04/21 23:40:14 I don't think that is needed. This function doesn'
bokan 2016/04/22 00:52:54 Whoops, yah, you're right. That's fine.
92 if (isInDocument(exitedTarget)) {
93 Node* exitedNode = exitedTarget->toNode();
bokan 2016/04/21 21:38:28 Either DCHECK that exitedNode != nullptr (if indee
Navid Zolghadr 2016/04/21 23:40:14 Neither the DCHECK nor the behavior of handling wh
bokan 2016/04/22 00:52:54 Acknowledged.
Navid Zolghadr 2016/04/22 17:04:12 Done.
94 exitedNode->updateDistribution();
95 for (Node* node = exitedNode; node; node = FlatTreeTraversal::parent(*no de))
96 exitedAncestors.append(node);
97 }
98
99 if (isInDocument(enteredTarget)) {
100 Node* enteredNode = enteredTarget->toNode();
101 enteredNode->updateDistribution();
bokan 2016/04/21 21:38:29 Question: what does updateDistribution do?
Navid Zolghadr 2016/04/21 23:40:14 To be honest with you, I'm not sure myself. From t
bokan 2016/04/22 00:52:54 Hmm, I've never seen it before. But I can see it w
102 for (Node* node = enteredNode; node; node = FlatTreeTraversal::parent(*n ode))
103 enteredAncestors.append(node);
104 }
105
106 exitedAncestorsCommonParentIndex = exitedAncestors.size();
107 enteredAncestorsCommonParentIndex = enteredAncestors.size();
bokan 2016/04/21 21:38:29 One more suggested DCHECK here :), exitedAncestors
Navid Zolghadr 2016/04/21 23:40:14 That's not right. They might be from different doc
bokan 2016/04/22 00:52:54 That's what I'm getting at, you have no way of dis
Navid Zolghadr 2016/04/22 01:46:13 No. That is not the case. Here is an example if we
bokan 2016/04/22 01:50:47 Bah, I'm not having a good day...you're totally ri
108 while (exitedAncestorsCommonParentIndex > 0
109 && enteredAncestorsCommonParentIndex > 0) {
110 if (exitedAncestors[exitedAncestorsCommonParentIndex-1]
111 != enteredAncestors[enteredAncestorsCommonParentIndex-1])
112 break;
113 exitedAncestorsCommonParentIndex--;
114 enteredAncestorsCommonParentIndex--;
115 }
116 }
117
82 } // namespace 118 } // namespace
83 119
84 WebInputEventResult PointerEventManager::dispatchPointerEvent( 120 WebInputEventResult PointerEventManager::dispatchPointerEvent(
85 EventTarget* target, 121 EventTarget* target,
86 PointerEvent* pointerEvent, 122 PointerEvent* pointerEvent,
87 bool checkForListener) 123 bool checkForListener)
88 { 124 {
89 if (!target) 125 if (!target)
90 return WebInputEventResult::NotHandled; 126 return WebInputEventResult::NotHandled;
91 127
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 pointerEvent, EventTypeNames::pointerout, enteredTarget)); 209 pointerEvent, EventTypeNames::pointerout, enteredTarget));
174 } else { 210 } else {
175 dispatchMouseEvent(exitedTarget, 211 dispatchMouseEvent(exitedTarget,
176 EventTypeNames::mouseout, 212 EventTypeNames::mouseout,
177 mouseEventWithRegion(exitedTarget->toNode(), mouseEvent), 213 mouseEventWithRegion(exitedTarget->toNode(), mouseEvent),
178 enteredTarget); 214 enteredTarget);
179 } 215 }
180 } 216 }
181 217
182 // Create lists of all exited/entered ancestors, locate the common ancestor 218 // Create lists of all exited/entered ancestors, locate the common ancestor
183 HeapVector<Member<Node>, 32> exitedAncestors; 219 // Based on httparchive, in more than 97% cases the depth of DOM is less
bokan 2016/04/21 21:38:29 Doesn't this mean that in 3% of cases we'll incur
Navid Zolghadr 2016/04/21 23:40:14 I don't have a strong answer here. I had it at 32.
bokan 2016/04/22 00:52:54 Heap allocations can be fairly expensive and unpre
184 HeapVector<Member<Node>, 32> enteredAncestors; 220 // than 20.
185 if (isInDocument(exitedTarget)) { 221 HeapVector<Member<Node>, 20> exitedAncestors;
186 Node* exitedNode = exitedTarget->toNode(); 222 HeapVector<Member<Node>, 20> enteredAncestors;
187 exitedNode->updateDistribution(); 223 size_t exitedAncestorsCommonParentIndex = 0;
188 for (Node* node = exitedNode; node; node = FlatTreeTraversal::parent(*no de)) 224 size_t enteredAncestorsCommonParentIndex = 0;
189 exitedAncestors.append(node);
190 }
191
192 if (isInDocument(enteredTarget)) {
193 Node* enteredNode = enteredTarget->toNode();
194 enteredNode->updateDistribution();
195 for (Node* node = enteredNode; node; node = FlatTreeTraversal::parent(*n ode))
196 enteredAncestors.append(node);
197 }
198 225
199 // A note on mouseenter and mouseleave: These are non-bubbling events, and t hey are dispatched if there 226 // A note on mouseenter and mouseleave: These are non-bubbling events, and t hey are dispatched if there
200 // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special 227 // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special
201 // handling is necessary to avoid O(n^2) capturing event handler checks. 228 // handling is necessary to avoid O(n^2) capturing event handler checks.
202 // 229 //
203 // Note, however, that this optimization can possibly cause some unanswere d/missing/redundant mouseenter or 230 // Note, however, that this optimization can possibly cause some unanswere d/missing/redundant mouseenter or
204 // mouseleave events in certain contrived eventhandling scenarios, e.g., whe n: 231 // mouseleave events in certain contrived eventhandling scenarios, e.g., whe n:
205 // - the mouseleave handler for a node sets the only capturing-mouseleave-li stener in its ancestor, or 232 // - the mouseleave handler for a node sets the only capturing-mouseleave-li stener in its ancestor, or
206 // - DOM mods in any mouseenter/mouseleave handler changes the common ancest or of exited & entered nodes, etc. 233 // - DOM mods in any mouseenter/mouseleave handler changes the common ancest or of exited & entered nodes, etc.
207 // We think the spec specifies a "frozen" state to avoid such corner cases ( check the discussion on "candidate event 234 // We think the spec specifies a "frozen" state to avoid such corner cases ( check the discussion on "candidate event
208 // listeners" at http://www.w3.org/TR/uievents), but our code below preserve s one such behavior from past only to 235 // listeners" at http://www.w3.org/TR/uievents), but our code below preserve s one such behavior from past only to
209 // match Firefox and IE behavior. 236 // match Firefox and IE behavior.
210 // 237 //
211 // TODO(mustaq): Confirm spec conformance, double-check with other browsers. 238 // TODO(mustaq): Confirm spec conformance, double-check with other browsers.
212 239
213 size_t numExitedAncestors = exitedAncestors.size(); 240 buildAncestorChainsAndFindCommonAncestors(
214 size_t numEnteredAncestors = enteredAncestors.size(); 241 exitedTarget, enteredTarget,
215 242 exitedAncestors, enteredAncestors,
216 size_t exitedAncestorIndex = numExitedAncestors; 243 exitedAncestorsCommonParentIndex, enteredAncestorsCommonParentIndex);
217 size_t enteredAncestorIndex = numEnteredAncestors;
218 for (size_t i = 0; i < numExitedAncestors; i++) {
219 for (size_t j = 0; j < numEnteredAncestors; j++) {
220 if (exitedAncestors[i] == enteredAncestors[j]) {
221 exitedAncestorIndex = i;
222 enteredAncestorIndex = j;
223 break;
224 }
225 }
226 if (exitedAncestorIndex < exitedAncestors.size())
227 break;
228 }
229 244
230 bool exitedNodeHasCapturingAncestor = false; 245 bool exitedNodeHasCapturingAncestor = false;
231 for (size_t j = 0; j < exitedAncestors.size(); j++) { 246 for (size_t j = 0; j < exitedAncestors.size(); j++) {
232 if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouse leave) 247 if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouse leave)
233 || (RuntimeEnabledFeatures::pointerEventEnabled() 248 || (RuntimeEnabledFeatures::pointerEventEnabled()
234 && exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::po interleave))) 249 && exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::po interleave)))
235 exitedNodeHasCapturingAncestor = true; 250 exitedNodeHasCapturingAncestor = true;
236 } 251 }
237 252
238 // Dispatch pointerleave/mouseleave events, in child-to-parent order. 253 // Dispatch pointerleave/mouseleave events, in child-to-parent order.
239 for (size_t j = 0; j < exitedAncestorIndex; j++) { 254 for (size_t j = 0; j < exitedAncestorsCommonParentIndex; j++) {
240 if (!sendMouseEvent) { 255 if (!sendMouseEvent) {
241 dispatchPointerEvent(exitedAncestors[j].get(), 256 dispatchPointerEvent(exitedAncestors[j].get(),
242 m_pointerEventFactory.createPointerTransitionEvent( 257 m_pointerEventFactory.createPointerTransitionEvent(
243 pointerEvent, EventTypeNames::pointerleave, enteredTarget), 258 pointerEvent, EventTypeNames::pointerleave, enteredTarget),
244 !exitedNodeHasCapturingAncestor); 259 !exitedNodeHasCapturingAncestor);
245 } else { 260 } else {
246 dispatchMouseEvent(exitedAncestors[j].get(), 261 dispatchMouseEvent(exitedAncestors[j].get(),
247 EventTypeNames::mouseleave, 262 EventTypeNames::mouseleave,
248 mouseEventWithRegion(exitedTarget->toNode(), mouseEvent), 263 mouseEventWithRegion(exitedTarget->toNode(), mouseEvent),
249 enteredTarget, 0, !exitedNodeHasCapturingAncestor); 264 enteredTarget, 0, !exitedNodeHasCapturingAncestor);
(...skipping 15 matching lines...) Expand all
265 // the leave handlers might set a capturing enter handler. 280 // the leave handlers might set a capturing enter handler.
266 bool enteredNodeHasCapturingAncestor = false; 281 bool enteredNodeHasCapturingAncestor = false;
267 for (size_t i = 0; i < enteredAncestors.size(); i++) { 282 for (size_t i = 0; i < enteredAncestors.size(); i++) {
268 if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mous eenter) 283 if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mous eenter)
269 || (RuntimeEnabledFeatures::pointerEventEnabled() 284 || (RuntimeEnabledFeatures::pointerEventEnabled()
270 && enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::p ointerenter))) 285 && enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::p ointerenter)))
271 enteredNodeHasCapturingAncestor = true; 286 enteredNodeHasCapturingAncestor = true;
272 } 287 }
273 288
274 // Dispatch pointerenter/mouseenter events, in parent-to-child order. 289 // Dispatch pointerenter/mouseenter events, in parent-to-child order.
275 for (size_t i = enteredAncestorIndex; i > 0; i--) { 290 for (size_t i = enteredAncestorsCommonParentIndex; i > 0; i--) {
276 if (!sendMouseEvent) { 291 if (!sendMouseEvent) {
277 dispatchPointerEvent(enteredAncestors[i-1].get(), 292 dispatchPointerEvent(enteredAncestors[i-1].get(),
278 m_pointerEventFactory.createPointerTransitionEvent( 293 m_pointerEventFactory.createPointerTransitionEvent(
279 pointerEvent, EventTypeNames::pointerenter, exitedTarget), 294 pointerEvent, EventTypeNames::pointerenter, exitedTarget),
280 !enteredNodeHasCapturingAncestor); 295 !enteredNodeHasCapturingAncestor);
281 } else { 296 } else {
282 dispatchMouseEvent(enteredAncestors[i-1].get(), 297 dispatchMouseEvent(enteredAncestors[i-1].get(),
283 EventTypeNames::mouseenter, mouseEvent, exitedTarget, 298 EventTypeNames::mouseenter, mouseEvent, exitedTarget,
284 0, !enteredNodeHasCapturingAncestor); 299 0, !enteredNodeHasCapturingAncestor);
285 } 300 }
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after
632 647
633 DEFINE_TRACE(PointerEventManager) 648 DEFINE_TRACE(PointerEventManager)
634 { 649 {
635 visitor->trace(m_nodeUnderPointer); 650 visitor->trace(m_nodeUnderPointer);
636 visitor->trace(m_pointerCaptureTarget); 651 visitor->trace(m_pointerCaptureTarget);
637 visitor->trace(m_pendingPointerCaptureTarget); 652 visitor->trace(m_pendingPointerCaptureTarget);
638 } 653 }
639 654
640 655
641 } // namespace blink 656 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698