Changeset 83386 in webkit
- Timestamp:
- Apr 9, 2011 7:33:19 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r83383 r83386 1 2011-04-02 Dimitri Glazkov <dglazkov@chromium.org> 2 3 Reviewed by Ojan Vafai. 4 5 Implement proper handling of mouseover/mouseout events in regard to shadow DOM boundaries. 6 https://bugs.webkit.org/show_bug.cgi?id=55515 7 8 * fast/events/shadow-boundary-crossing-expected.txt: Updated expectations. 9 * fast/events/shadow-boundary-crossing.html: Added new test for mouseover/mouseout handling. 10 1 11 2011-04-09 Dirk Pranke <dpranke@chromium.org> 2 12 -
trunk/LayoutTests/fast/events/shadow-boundary-crossing-expected.txt
r78077 r83386 5 5 Mutation events should not propagate out of the shadow DOM: PASS 6 6 The selectstart event should not propagate out of the shadow DOM: PASS 7 The mouseover/mouseout event between two elements inside the same shadow subtree should not propagate out of the shadow DOM: PASS 7 8 Label should look beyond shadow boundary to detect if it encloses its associated element: PASS 8 9 Events for default event handler should not be retargeted: PASS -
trunk/LayoutTests/fast/events/shadow-boundary-crossing.html
r78077 r83386 20 20 } 21 21 22 function moveOverLeftQuarterOf(element) 23 { 24 if (!window.eventSender) 25 return; 26 27 var x = element.offsetLeft + element.offsetWidth / 4; 28 var y = element.offsetTop + element.offsetHeight / 2; 29 eventSender.mouseMoveTo(x, y); 30 } 31 32 function moveOverRightQuarterOf(element) 33 { 34 if (!window.eventSender) 35 return; 36 37 var x = element.offsetLeft + element.offsetWidth * 3 / 4; 38 var y = element.offsetTop + element.offsetHeight / 2; 39 eventSender.mouseMoveTo(x, y); 40 } 41 22 42 function clickOn(element) 23 43 { … … 32 52 return; 33 53 34 var x = element.offsetLeft + element.offsetWidth / 4; 35 var y = element.offsetTop + element.offsetHeight / 2; 36 eventSender.mouseMoveTo(x, y); 54 moveOverLeftQuarterOf(element); 37 55 eventSender.mouseDown(); 38 56 eventSender.mouseUp(); … … 74 92 textInput.parentNode.removeChild(textInput); 75 93 document.selectstart = null; 94 }, 95 mouseOverAndOutPropagation: function() 96 { 97 var count = 0; 98 var fileInput = document.body.appendChild(document.createElement('input')); 99 fileInput.setAttribute('type', 'file'); 100 var countEventDispatch = function() 101 { 102 count++; 103 } 104 moveOverLeftQuarterOf(fileInput); 105 106 document.body.addEventListener('mouseover', countEventDispatch, false); 107 document.body.addEventListener('mouseout', countEventDispatch, false); 108 109 moveOverRightQuarterOf(fileInput); 110 111 log("The mouseover/mouseout event between two elements inside the same shadow subtree should not propagate out of the shadow DOM", count == 0); 112 113 document.body.removeEventListener('mouseover', countEventDispatch, false); 114 document.body.removeEventListener('mouseout', countEventDispatch, false); 115 fileInput.parentNode.removeChild(fileInput); 76 116 }, 77 117 labelSyntheticClick: function() -
trunk/Source/WebCore/ChangeLog
r83385 r83386 1 2011-04-08 Dimitri Glazkov <dglazkov@chromium.org> 2 3 Reviewed by Ojan Vafai. 4 5 Implement proper handling of mouseover/mouseout events in regard to shadow DOM boundaries. 6 https://bugs.webkit.org/show_bug.cgi?id=55515 7 8 This implements XBL 2.0's specified handling of mouseover/mouseout events: 9 http://dev.w3.org/2006/xbl2/Overview.html#the-mouseover-and-mouseout-events 10 11 To do this, we: 12 1) calculate lowest common ancestor between relatedTarget and target, and 13 the nearest boundaries around them: the outer (common) boundary, and the 14 inner (specific to relatedTarget) boundary. Then, we 15 2) ensure that events only propagate up to the common boundary (or 16 all the way if boundary is not found), while 17 3) updating relatedTarget be the inner boundary. 18 19 We also detect the most common case when no common boundary could exist 20 and provide a fast path to short-circuit most of the boundary detection 21 logic. 22 23 Test: fast/events/shadow-boundary-crossing.html 24 25 * dom/EventDispatcher.cpp: 26 (WebCore::EventDispatcher::adjustToShadowBoundaries): Added a helper to determine lowest 27 common ancestor, the boundaries around it, and compute adjustments 28 to relatedTarget and event target ancestor chain. 29 (WebCore::ancestorsCrossShadowBoundaries): Added. 30 (WebCore::EventDispatcher::adjustRelatedTarget): Changed to calculate 31 inner/outer shadow DOM boundaries and adjust ancestors chain accordingly. 32 (WebCore::EventDispatcher::EventDispatcher): Added flag initializer 33 (WebCore::EventDispatcher::ensureEventAncestors): Renamed from getEventAncestors, 34 converted to use initialization flag, rather than testing for empty. 35 * dom/EventDispatcher.h: Adjusted decls. 36 * dom/MouseEvent.cpp: 37 (WebCore::MouseEventDispatchMediator::dispatchEvent): Changed to send event 38 to adjustRelatedTarget. 39 1 40 2011-04-08 Geoffrey Garen <ggaren@apple.com> 2 41 -
trunk/Source/WebCore/dom/EventDispatcher.cpp
r83298 r83386 123 123 } 124 124 125 PassRefPtr<EventTarget> EventDispatcher::adjustToShadowBoundaries(PassRefPtr<Node> relatedTarget, const Vector<Node*> relatedTargetAncestors) 126 { 127 Vector<EventContext>::const_iterator lowestCommonBoundary = m_ancestors.end(); 128 // Assume divergent boundary is the relatedTarget itself (in other words, related target ancestor chain does not cross any shadow DOM boundaries). 129 Vector<Node*>::const_iterator firstDivergentBoundary = relatedTargetAncestors.begin(); 130 131 Vector<EventContext>::const_iterator targetAncestor = m_ancestors.end(); 132 // Walk down from the top, looking for lowest common ancestor, also monitoring shadow DOM boundaries. 133 bool diverged = false; 134 for (Vector<Node*>::const_iterator i = relatedTargetAncestors.end() - 1; i >= relatedTargetAncestors.begin(); --i) { 135 if (diverged) { 136 if ((*i)->isShadowRoot()) { 137 firstDivergentBoundary = i + 1; 138 break; 139 } 140 continue; 141 } 142 143 if (targetAncestor == m_ancestors.begin()) { 144 diverged = true; 145 continue; 146 } 147 148 targetAncestor--; 149 150 if ((*i)->isShadowRoot()) 151 lowestCommonBoundary = targetAncestor; 152 153 if ((*i) != (*targetAncestor).node()) 154 diverged = true; 155 } 156 157 if (!diverged) { 158 // The relatedTarget is a parent or shadowHost of the target. 159 if (m_node->isShadowRoot()) 160 lowestCommonBoundary = m_ancestors.begin(); 161 } else if ((*firstDivergentBoundary) == m_node.get()) { 162 // Since ancestors does not contain target itself, we must account 163 // for the possibility that target is a shadowHost of relatedTarget 164 // and thus serves as the lowestCommonBoundary. 165 // Luckily, in this case the firstDivergentBoundary is target. 166 lowestCommonBoundary = m_ancestors.begin(); 167 } 168 169 // Trim ancestors to lowestCommonBoundary to keep events inside of the common shadow DOM subtree. 170 if (lowestCommonBoundary != m_ancestors.end()) 171 m_ancestors.shrink(lowestCommonBoundary - m_ancestors.begin()); 172 // Set event's related target to the first encountered shadow DOM boundary in the divergent subtree. 173 return firstDivergentBoundary != relatedTargetAncestors.begin() ? *firstDivergentBoundary : relatedTarget; 174 } 175 176 inline static bool ancestorsCrossShadowBoundaries(const Vector<EventContext>& ancestors) 177 { 178 return ancestors.isEmpty() || ancestors.first().node() == ancestors.last().node(); 179 } 180 125 181 // FIXME: Once https://bugs.webkit.org/show_bug.cgi?id=52963 lands, this should 126 182 // be greatly improved. See https://bugs.webkit.org/show_bug.cgi?id=54025. 127 PassRefPtr<EventTarget> EventDispatcher::adjustRelatedTarget(PassRefPtr<EventTarget> relatedTarget) 128 { 183 PassRefPtr<EventTarget> EventDispatcher::adjustRelatedTarget(Event* event, PassRefPtr<EventTarget> prpRelatedTarget) 184 { 185 if (!prpRelatedTarget) 186 return 0; 187 188 RefPtr<Node> relatedTarget = prpRelatedTarget->toNode(); 129 189 if (!relatedTarget) 130 190 return 0; 131 191 132 Node* node = relatedTarget->toNode(); 133 if (!node) 134 return relatedTarget; 135 136 Node* outermostShadowBoundary = node; 137 for (Node* n = node; n; n = n->parentOrHostNode()) { 192 Node* target = m_node.get(); 193 if (!target) 194 return prpRelatedTarget; 195 196 ensureEventAncestors(event); 197 198 // Calculate early if the common boundary is even possible by looking at 199 // ancestors size and if the retargeting has occured (indicating the presence of shadow DOM boundaries). 200 // If there are no boundaries detected, the target and related target can't have a common boundary. 201 bool noCommonBoundary = ancestorsCrossShadowBoundaries(m_ancestors); 202 203 Vector<Node*> relatedTargetAncestors; 204 Node* outermostShadowBoundary = relatedTarget.get(); 205 for (Node* n = outermostShadowBoundary; n; n = n->parentOrHostNode()) { 138 206 if (n->isShadowRoot()) 139 207 outermostShadowBoundary = n->parentOrHostNode(); 140 } 141 return outermostShadowBoundary; 208 if (!noCommonBoundary) 209 relatedTargetAncestors.append(n); 210 } 211 212 // Short-circuit the fast case when we know there is no need to calculate a common boundary. 213 if (noCommonBoundary) 214 return outermostShadowBoundary; 215 216 return adjustToShadowBoundaries(relatedTarget.release(), relatedTargetAncestors); 142 217 } 143 218 144 219 EventDispatcher::EventDispatcher(Node* node) 145 220 : m_node(node) 221 , m_ancestorsInitialized(false) 146 222 { 147 223 ASSERT(node); … … 149 225 } 150 226 151 void EventDispatcher::getEventAncestors(EventTarget* originalTarget, EventDispatchBehavior behavior) 152 { 227 void EventDispatcher::ensureEventAncestors(Event* event) 228 { 229 EventDispatchBehavior behavior = determineDispatchBehavior(event); 230 153 231 if (!m_node->inDocument()) 154 232 return; 155 233 156 if ( ancestorsInitialized())234 if (m_ancestorsInitialized) 157 235 return; 158 236 159 EventTarget* target = originalTarget; 237 m_ancestorsInitialized = true; 238 160 239 Node* ancestor = m_node.get(); 240 EventTarget* target = eventTargetRespectingSVGTargetRules(ancestor); 161 241 bool shouldSkipNextAncestor = false; 162 242 while (true) { … … 193 273 194 274 RefPtr<EventTarget> originalTarget = event->target(); 195 getEventAncestors(originalTarget.get(), determineDispatchBehavior(event.get()));275 ensureEventAncestors(event.get()); 196 276 197 277 WindowEventContext windowContext(event.get(), m_node.get(), topEventContext()); … … 277 357 } 278 358 279 280 359 const EventContext* EventDispatcher::topEventContext() 281 360 { 282 361 return m_ancestors.isEmpty() ? 0 : &m_ancestors.last(); 283 }284 285 bool EventDispatcher::ancestorsInitialized() const286 {287 return m_ancestors.size();288 362 } 289 363 -
trunk/Source/WebCore/dom/EventDispatcher.h
r83298 r83386 55 55 56 56 bool dispatchEvent(PassRefPtr<Event>); 57 PassRefPtr<EventTarget> adjustRelatedTarget( PassRefPtr<EventTarget>);57 PassRefPtr<EventTarget> adjustRelatedTarget(Event*, PassRefPtr<EventTarget>); 58 58 Node* node() const; 59 59 … … 61 61 EventDispatcher(Node*); 62 62 63 PassRefPtr<EventTarget> adjustToShadowBoundaries(PassRefPtr<Node> relatedTarget, const Vector<Node*> relatedTargetAncestors); 63 64 EventDispatchBehavior determineDispatchBehavior(Event*); 64 void getEventAncestors(EventTarget* originalTarget, EventDispatchBehavior);65 void ensureEventAncestors(Event*); 65 66 const EventContext* topEventContext(); 66 bool ancestorsInitialized() const;67 67 68 68 Vector<EventContext> m_ancestors; … … 70 70 RefPtr<EventTarget> m_originalTarget; 71 71 RefPtr<FrameView> m_view; 72 bool m_ancestorsInitialized; 72 73 }; 73 74 -
trunk/Source/WebCore/dom/MouseEvent.cpp
r82948 r83386 171 171 return false; // Shouldn't happen. 172 172 173 RefPtr<EventTarget> relatedTarget = dispatcher->adjustRelatedTarget(event() ->relatedTarget());173 RefPtr<EventTarget> relatedTarget = dispatcher->adjustRelatedTarget(event(), event()->relatedTarget()); 174 174 event()->setRelatedTarget(relatedTarget); 175 175
Note: See TracChangeset
for help on using the changeset viewer.