1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "EventRetargeter.h" 22 23#include "ContainerNode.h" 24#include "EventContext.h" 25#include "EventPathWalker.h" 26#include "FocusEvent.h" 27#include "MouseEvent.h" 28#include "ShadowRoot.h" 29#include "Touch.h" 30#include "TouchEvent.h" 31#include "TouchList.h" 32#include "TreeScope.h" 33#include <wtf/PassRefPtr.h> 34#include <wtf/RefPtr.h> 35#include <wtf/Vector.h> 36 37namespace WebCore { 38 39static inline bool inTheSameScope(ShadowRoot* shadowRoot, EventTarget* target) 40{ 41 return target->toNode() && target->toNode()->treeScope()->rootNode() == shadowRoot; 42} 43 44static inline EventDispatchBehavior determineDispatchBehavior(Event* event, ShadowRoot* shadowRoot, EventTarget* target) 45{ 46#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) 47 // Video-only full screen is a mode where we use the shadow DOM as an implementation 48 // detail that should not be detectable by the web content. 49 if (Element* element = target->toNode()->document()->webkitCurrentFullScreenElement()) { 50 // FIXME: We assume that if the full screen element is a media element that it's 51 // the video-only full screen. Both here and elsewhere. But that is probably wrong. 52 if (element->isMediaElement() && shadowRoot && shadowRoot->host() == element) 53 return StayInsideShadowDOM; 54 } 55#else 56 UNUSED_PARAM(shadowRoot); 57#endif 58 59 // WebKit never allowed selectstart event to cross the the shadow DOM boundary. 60 // Changing this breaks existing sites. 61 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. 62 const AtomicString eventType = event->type(); 63 if (inTheSameScope(shadowRoot, target) 64 && (eventType == eventNames().abortEvent 65 || eventType == eventNames().changeEvent 66 || eventType == eventNames().errorEvent 67 || eventType == eventNames().loadEvent 68 || eventType == eventNames().resetEvent 69 || eventType == eventNames().resizeEvent 70 || eventType == eventNames().scrollEvent 71 || eventType == eventNames().selectEvent 72 || eventType == eventNames().selectstartEvent)) 73 return StayInsideShadowDOM; 74 75 return RetargetEvent; 76} 77 78void EventRetargeter::calculateEventPath(Node* node, Event* event, EventPath& eventPath) 79{ 80 bool inDocument = node->inDocument(); 81 bool isSVGElement = node->isSVGElement(); 82 bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent(); 83#if ENABLE(TOUCH_EVENTS) 84 bool isTouchEvent = event->isTouchEvent(); 85#endif 86 Vector<EventTarget*, 32> targetStack; 87 for (EventPathWalker walker(node); walker.node(); walker.moveToParent()) { 88 Node* node = walker.node(); 89 if (targetStack.isEmpty()) 90 targetStack.append(eventTargetRespectingTargetRules(node)); 91 else if (walker.isVisitingInsertionPointInReprojection()) 92 targetStack.append(targetStack.last()); 93 if (isMouseOrFocusEvent) 94 eventPath.append(adoptPtr(new MouseOrFocusEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); 95#if ENABLE(TOUCH_EVENTS) 96 else if (isTouchEvent) 97 eventPath.append(adoptPtr(new TouchEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); 98#endif 99 else 100 eventPath.append(adoptPtr(new EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); 101 if (!inDocument) 102 return; 103 if (!node->isShadowRoot()) 104 continue; 105 if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) 106 return; 107 if (!isSVGElement) { 108 ASSERT(!targetStack.isEmpty()); 109 targetStack.removeLast(); 110 } 111 } 112} 113 114void EventRetargeter::adjustForMouseEvent(Node* node, const MouseEvent& mouseEvent, EventPath& eventPath) 115{ 116 adjustForRelatedTarget(node, mouseEvent.relatedTarget(), eventPath); 117} 118 119void EventRetargeter::adjustForFocusEvent(Node* node, const FocusEvent& focusEvent, EventPath& eventPath) 120{ 121 adjustForRelatedTarget(node, focusEvent.relatedTarget(), eventPath); 122} 123 124#if ENABLE(TOUCH_EVENTS) 125void EventRetargeter::adjustForTouchEvent(Node* node, const TouchEvent& touchEvent, EventPath& eventPath) 126{ 127 size_t eventPathSize = eventPath.size(); 128 129 EventPathTouchLists eventPathTouches(eventPathSize); 130 EventPathTouchLists eventPathTargetTouches(eventPathSize); 131 EventPathTouchLists eventPathChangedTouches(eventPathSize); 132 133 for (size_t i = 0; i < eventPathSize; ++i) { 134 ASSERT(eventPath[i]->isTouchEventContext()); 135 TouchEventContext* touchEventContext = toTouchEventContext(eventPath[i].get()); 136 eventPathTouches[i] = touchEventContext->touches(); 137 eventPathTargetTouches[i] = touchEventContext->targetTouches(); 138 eventPathChangedTouches[i] = touchEventContext->changedTouches(); 139 } 140 141 adjustTouchList(node, touchEvent.touches(), eventPath, eventPathTouches); 142 adjustTouchList(node, touchEvent.targetTouches(), eventPath, eventPathTargetTouches); 143 adjustTouchList(node, touchEvent.changedTouches(), eventPath, eventPathChangedTouches); 144} 145 146void EventRetargeter::adjustTouchList(const Node* node, const TouchList* touchList, const EventPath& eventPath, EventPathTouchLists& eventPathTouchLists) 147{ 148 if (!touchList) 149 return; 150 size_t eventPathSize = eventPath.size(); 151 ASSERT(eventPathTouchLists.size() == eventPathSize); 152 for (size_t i = 0; i < touchList->length(); ++i) { 153 const Touch& touch = *touchList->item(i); 154 AdjustedNodes adjustedNodes; 155 calculateAdjustedNodes(node, touch.target()->toNode(), DoesNotStopAtBoundary, const_cast<EventPath&>(eventPath), adjustedNodes); 156 ASSERT(adjustedNodes.size() == eventPathSize); 157 for (size_t j = 0; j < eventPathSize; ++j) 158 eventPathTouchLists[j]->append(touch.cloneWithNewTarget(adjustedNodes[j].get())); 159 } 160} 161#endif 162 163void EventRetargeter::adjustForRelatedTarget(const Node* node, EventTarget* relatedTarget, EventPath& eventPath) 164{ 165 if (!node) 166 return; 167 if (!relatedTarget) 168 return; 169 Node* relatedNode = relatedTarget->toNode(); 170 if (!relatedNode) 171 return; 172 AdjustedNodes adjustedNodes; 173 calculateAdjustedNodes(node, relatedNode, StopAtBoundaryIfNeeded, eventPath, adjustedNodes); 174 ASSERT(adjustedNodes.size() <= eventPath.size()); 175 for (size_t i = 0; i < adjustedNodes.size(); ++i) { 176 ASSERT(eventPath[i]->isMouseOrFocusEventContext()); 177 MouseOrFocusEventContext* mouseOrFocusEventContext = static_cast<MouseOrFocusEventContext*>(eventPath[i].get()); 178 mouseOrFocusEventContext->setRelatedTarget(adjustedNodes[i]); 179 } 180} 181 182void EventRetargeter::calculateAdjustedNodes(const Node* node, const Node* relatedNode, EventWithRelatedTargetDispatchBehavior eventWithRelatedTargetDispatchBehavior, EventPath& eventPath, AdjustedNodes& adjustedNodes) 183{ 184 RelatedNodeMap relatedNodeMap; 185 buildRelatedNodeMap(relatedNode, relatedNodeMap); 186 187 // Synthetic mouse events can have a relatedTarget which is identical to the target. 188 bool targetIsIdenticalToToRelatedTarget = (node == relatedNode); 189 190 TreeScope* lastTreeScope = 0; 191 Node* adjustedNode = 0; 192 for (EventPath::const_iterator iter = eventPath.begin(); iter < eventPath.end(); ++iter) { 193 TreeScope* scope = (*iter)->node()->treeScope(); 194 if (scope == lastTreeScope) { 195 // Re-use the previous adjustedRelatedTarget if treeScope does not change. Just for the performance optimization. 196 adjustedNodes.append(adjustedNode); 197 } else { 198 adjustedNode = findRelatedNode(scope, relatedNodeMap); 199 adjustedNodes.append(adjustedNode); 200 } 201 lastTreeScope = scope; 202 if (eventWithRelatedTargetDispatchBehavior == DoesNotStopAtBoundary) 203 continue; 204 if (targetIsIdenticalToToRelatedTarget) { 205 if (node->treeScope()->rootNode() == (*iter)->node()) { 206 eventPath.shrink(iter + 1 - eventPath.begin()); 207 break; 208 } 209 } else if ((*iter)->target() == adjustedNode) { 210 // Event dispatching should be stopped here. 211 eventPath.shrink(iter - eventPath.begin()); 212 adjustedNodes.shrink(adjustedNodes.size() - 1); 213 break; 214 } 215 } 216} 217 218void EventRetargeter::buildRelatedNodeMap(const Node* relatedNode, RelatedNodeMap& relatedNodeMap) 219{ 220 Vector<Node*, 32> relatedNodeStack; 221 TreeScope* lastTreeScope = 0; 222 for (EventPathWalker walker(relatedNode); walker.node(); walker.moveToParent()) { 223 Node* node = walker.node(); 224 if (relatedNodeStack.isEmpty()) 225 relatedNodeStack.append(node); 226 else if (walker.isVisitingInsertionPointInReprojection()) 227 relatedNodeStack.append(relatedNodeStack.last()); 228 TreeScope* scope = node->treeScope(); 229 // Skips adding a node to the map if treeScope does not change. Just for the performance optimization. 230 if (scope != lastTreeScope) 231 relatedNodeMap.add(scope, relatedNodeStack.last()); 232 lastTreeScope = scope; 233 if (node->isShadowRoot()) { 234 ASSERT(!relatedNodeStack.isEmpty()); 235 relatedNodeStack.removeLast(); 236 } 237 } 238} 239 240Node* EventRetargeter::findRelatedNode(TreeScope* scope, RelatedNodeMap& relatedNodeMap) 241{ 242 Vector<TreeScope*, 32> parentTreeScopes; 243 Node* relatedNode = 0; 244 while (scope) { 245 parentTreeScopes.append(scope); 246 RelatedNodeMap::const_iterator found = relatedNodeMap.find(scope); 247 if (found != relatedNodeMap.end()) { 248 relatedNode = found->value; 249 break; 250 } 251 scope = scope->parentTreeScope(); 252 } 253 for (Vector<TreeScope*, 32>::iterator iter = parentTreeScopes.begin(); iter < parentTreeScopes.end(); ++iter) 254 relatedNodeMap.add(*iter, relatedNode); 255 return relatedNode; 256} 257 258} 259