1 /* 2 * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 #include "config.h" 23 #include "core/rendering/HitTestResult.h" 24 25 #include "core/HTMLNames.h" 26 #include "core/XLinkNames.h" 27 #include "core/dom/DocumentMarkerController.h" 28 #include "core/dom/NodeRenderingTraversal.h" 29 #include "core/dom/shadow/ShadowRoot.h" 30 #include "core/editing/FrameSelection.h" 31 #include "core/fetch/ImageResource.h" 32 #include "core/frame/LocalFrame.h" 33 #include "core/html/HTMLAnchorElement.h" 34 #include "core/html/HTMLImageElement.h" 35 #include "core/html/HTMLInputElement.h" 36 #include "core/html/HTMLMediaElement.h" 37 #include "core/html/parser/HTMLParserIdioms.h" 38 #include "core/page/FrameTree.h" 39 #include "core/rendering/RenderImage.h" 40 #include "core/rendering/RenderTextFragment.h" 41 #include "core/svg/SVGElement.h" 42 #include "platform/scroll/Scrollbar.h" 43 44 namespace WebCore { 45 46 using namespace HTMLNames; 47 48 HitTestResult::HitTestResult() 49 : m_isOverWidget(false) 50 , m_isFirstLetter(false) 51 { 52 } 53 54 HitTestResult::HitTestResult(const LayoutPoint& point) 55 : m_hitTestLocation(point) 56 , m_pointInInnerNodeFrame(point) 57 , m_isOverWidget(false) 58 , m_isFirstLetter(false) 59 { 60 } 61 62 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) 63 : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding) 64 , m_pointInInnerNodeFrame(centerPoint) 65 , m_isOverWidget(false) 66 , m_isFirstLetter(false) 67 { 68 } 69 70 HitTestResult::HitTestResult(const HitTestLocation& other) 71 : m_hitTestLocation(other) 72 , m_pointInInnerNodeFrame(m_hitTestLocation.point()) 73 , m_isOverWidget(false) 74 , m_isFirstLetter(false) 75 { 76 } 77 78 HitTestResult::HitTestResult(const HitTestResult& other) 79 : m_hitTestLocation(other.m_hitTestLocation) 80 , m_innerNode(other.innerNode()) 81 , m_innerPossiblyPseudoNode(other.m_innerPossiblyPseudoNode) 82 , m_innerNonSharedNode(other.innerNonSharedNode()) 83 , m_pointInInnerNodeFrame(other.m_pointInInnerNodeFrame) 84 , m_localPoint(other.localPoint()) 85 , m_innerURLElement(other.URLElement()) 86 , m_scrollbar(other.scrollbar()) 87 , m_isOverWidget(other.isOverWidget()) 88 , m_isFirstLetter(other.m_isFirstLetter) 89 { 90 // Only copy the NodeSet in case of rect hit test. 91 m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0); 92 } 93 94 HitTestResult::~HitTestResult() 95 { 96 } 97 98 HitTestResult& HitTestResult::operator=(const HitTestResult& other) 99 { 100 m_hitTestLocation = other.m_hitTestLocation; 101 m_innerNode = other.innerNode(); 102 m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode(); 103 m_innerNonSharedNode = other.innerNonSharedNode(); 104 m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; 105 m_localPoint = other.localPoint(); 106 m_innerURLElement = other.URLElement(); 107 m_scrollbar = other.scrollbar(); 108 m_isFirstLetter = other.m_isFirstLetter; 109 m_isOverWidget = other.isOverWidget(); 110 111 // Only copy the NodeSet in case of rect hit test. 112 m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0); 113 114 return *this; 115 } 116 117 RenderObject* HitTestResult::renderer() const 118 { 119 if (!m_innerNode) 120 return 0; 121 RenderObject* renderer = m_innerNode->renderer(); 122 if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment()) 123 return renderer; 124 return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter(); 125 } 126 127 void HitTestResult::setToNodesInDocumentTreeScope() 128 { 129 if (Node* node = innerNode()) { 130 node = node->document().ancestorInThisScope(node); 131 setInnerNode(node); 132 } 133 134 if (Node* node = innerNonSharedNode()) { 135 node = node->document().ancestorInThisScope(node); 136 setInnerNonSharedNode(node); 137 } 138 } 139 140 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot() 141 { 142 if (Node* node = innerNode()) { 143 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) { 144 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot) 145 setInnerNode(node->shadowHost()); 146 } 147 } 148 149 if (Node* node = innerNonSharedNode()) { 150 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) { 151 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot) 152 setInnerNonSharedNode(node->shadowHost()); 153 } 154 } 155 } 156 157 void HitTestResult::setInnerNode(Node* n) 158 { 159 m_innerPossiblyPseudoNode = n; 160 if (n && n->isPseudoElement()) 161 n = n->parentOrShadowHostNode(); 162 m_innerNode = n; 163 } 164 165 void HitTestResult::setInnerNonSharedNode(Node* n) 166 { 167 if (n && n->isPseudoElement()) 168 n = n->parentOrShadowHostNode(); 169 m_innerNonSharedNode = n; 170 } 171 172 void HitTestResult::setURLElement(Element* n) 173 { 174 m_innerURLElement = n; 175 } 176 177 void HitTestResult::setScrollbar(Scrollbar* s) 178 { 179 m_scrollbar = s; 180 } 181 182 LocalFrame* HitTestResult::innerNodeFrame() const 183 { 184 if (m_innerNonSharedNode) 185 return m_innerNonSharedNode->document().frame(); 186 if (m_innerNode) 187 return m_innerNode->document().frame(); 188 return 0; 189 } 190 191 bool HitTestResult::isSelected() const 192 { 193 if (!m_innerNonSharedNode) 194 return false; 195 196 if (LocalFrame* frame = m_innerNonSharedNode->document().frame()) 197 return frame->selection().contains(m_hitTestLocation.point()); 198 return false; 199 } 200 201 String HitTestResult::spellingToolTip(TextDirection& dir) const 202 { 203 dir = LTR; 204 // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar 205 // currently supply strings, but maybe someday markers associated with misspelled words will also. 206 if (!m_innerNonSharedNode) 207 return String(); 208 209 DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar); 210 if (!marker) 211 return String(); 212 213 if (RenderObject* renderer = m_innerNonSharedNode->renderer()) 214 dir = renderer->style()->direction(); 215 return marker->description(); 216 } 217 218 String HitTestResult::title(TextDirection& dir) const 219 { 220 dir = LTR; 221 // Find the title in the nearest enclosing DOM node. 222 // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it. 223 for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) { 224 if (titleNode->isElementNode()) { 225 String title = toElement(titleNode)->title(); 226 if (!title.isEmpty()) { 227 if (RenderObject* renderer = titleNode->renderer()) 228 dir = renderer->style()->direction(); 229 return title; 230 } 231 } 232 } 233 return String(); 234 } 235 236 const AtomicString& HitTestResult::altDisplayString() const 237 { 238 if (!m_innerNonSharedNode) 239 return nullAtom; 240 241 if (isHTMLImageElement(*m_innerNonSharedNode)) { 242 HTMLImageElement& image = toHTMLImageElement(*m_innerNonSharedNode); 243 return image.getAttribute(altAttr); 244 } 245 246 if (isHTMLInputElement(*m_innerNonSharedNode)) { 247 HTMLInputElement& input = toHTMLInputElement(*m_innerNonSharedNode); 248 return input.alt(); 249 } 250 251 return nullAtom; 252 } 253 254 Image* HitTestResult::image() const 255 { 256 if (!m_innerNonSharedNode) 257 return 0; 258 259 RenderObject* renderer = m_innerNonSharedNode->renderer(); 260 if (renderer && renderer->isImage()) { 261 RenderImage* image = toRenderImage(renderer); 262 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 263 return image->cachedImage()->imageForRenderer(image); 264 } 265 266 return 0; 267 } 268 269 IntRect HitTestResult::imageRect() const 270 { 271 if (!image()) 272 return IntRect(); 273 return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox(); 274 } 275 276 KURL HitTestResult::absoluteImageURL() const 277 { 278 return absoluteImageURLInternal(false); 279 } 280 281 KURL HitTestResult::absoluteImageURLIncludingCanvasDataURL() const 282 { 283 return absoluteImageURLInternal(true); 284 } 285 286 KURL HitTestResult::absoluteImageURLInternal(bool allowCanvas) const 287 { 288 if (!m_innerNonSharedNode) 289 return KURL(); 290 291 RenderObject* renderer = m_innerNonSharedNode->renderer(); 292 if (!(renderer && (renderer->isImage() || renderer->isCanvas()))) 293 return KURL(); 294 295 AtomicString urlString; 296 if ((allowCanvas && isHTMLCanvasElement(*m_innerNonSharedNode)) 297 || isHTMLEmbedElement(*m_innerNonSharedNode) 298 || isHTMLImageElement(*m_innerNonSharedNode) 299 || isHTMLInputElement(*m_innerNonSharedNode) 300 || isHTMLObjectElement(*m_innerNonSharedNode) 301 || isSVGImageElement(*m_innerNonSharedNode) 302 ) { 303 urlString = toElement(*m_innerNonSharedNode).imageSourceURL(); 304 } else 305 return KURL(); 306 307 return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); 308 } 309 310 KURL HitTestResult::absoluteMediaURL() const 311 { 312 if (HTMLMediaElement* mediaElt = mediaElement()) 313 return mediaElt->currentSrc(); 314 return KURL(); 315 } 316 317 HTMLMediaElement* HitTestResult::mediaElement() const 318 { 319 if (!m_innerNonSharedNode) 320 return 0; 321 322 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia())) 323 return 0; 324 325 if (isHTMLMediaElement(*m_innerNonSharedNode)) 326 return toHTMLMediaElement(m_innerNonSharedNode); 327 return 0; 328 } 329 330 KURL HitTestResult::absoluteLinkURL() const 331 { 332 if (!m_innerURLElement) 333 return KURL(); 334 335 AtomicString urlString; 336 if (isHTMLAnchorElement(*m_innerURLElement) || isHTMLAreaElement(*m_innerURLElement) || isHTMLLinkElement(*m_innerURLElement)) 337 urlString = m_innerURLElement->getAttribute(hrefAttr); 338 else if (isSVGAElement(*m_innerURLElement)) 339 urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr); 340 else 341 return KURL(); 342 343 return m_innerURLElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); 344 } 345 346 bool HitTestResult::isLiveLink() const 347 { 348 if (!m_innerURLElement) 349 return false; 350 351 if (isHTMLAnchorElement(*m_innerURLElement)) 352 return toHTMLAnchorElement(m_innerURLElement)->isLiveLink(); 353 354 if (isSVGAElement(*m_innerURLElement)) 355 return m_innerURLElement->isLink(); 356 357 return false; 358 } 359 360 bool HitTestResult::isMisspelled() const 361 { 362 if (!targetNode() || !targetNode()->renderer()) 363 return false; 364 VisiblePosition pos(targetNode()->renderer()->positionForPoint(localPoint())); 365 if (pos.isNull()) 366 return false; 367 return m_innerNonSharedNode->document().markers().markersInRange( 368 makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0; 369 } 370 371 bool HitTestResult::isOverLink() const 372 { 373 return m_innerURLElement && m_innerURLElement->isLink(); 374 } 375 376 String HitTestResult::textContent() const 377 { 378 if (!m_innerURLElement) 379 return String(); 380 return m_innerURLElement->textContent(); 381 } 382 383 // FIXME: This function needs a better name and may belong in a different class. It's not 384 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this 385 // function would make more sense in the ContextMenu class, except that WebElementDictionary 386 // hooks into it. Anyway, we should architect this better. 387 bool HitTestResult::isContentEditable() const 388 { 389 if (!m_innerNonSharedNode) 390 return false; 391 392 if (isHTMLTextAreaElement(*m_innerNonSharedNode)) 393 return true; 394 395 if (isHTMLInputElement(*m_innerNonSharedNode)) 396 return toHTMLInputElement(*m_innerNonSharedNode).isTextField(); 397 398 return m_innerNonSharedNode->rendererIsEditable(); 399 } 400 401 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect) 402 { 403 // If it is not a rect-based hit test, this method has to be no-op. 404 // Return false, so the hit test stops. 405 if (!isRectBasedTest()) 406 return false; 407 408 // If node is null, return true so the hit test can continue. 409 if (!node) 410 return true; 411 412 if (request.disallowsShadowContent()) 413 node = node->document().ancestorInThisScope(node); 414 415 mutableRectBasedTestResult().add(node); 416 417 bool regionFilled = rect.contains(locationInContainer.boundingBox()); 418 return !regionFilled; 419 } 420 421 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect) 422 { 423 // If it is not a rect-based hit test, this method has to be no-op. 424 // Return false, so the hit test stops. 425 if (!isRectBasedTest()) 426 return false; 427 428 // If node is null, return true so the hit test can continue. 429 if (!node) 430 return true; 431 432 if (request.disallowsShadowContent()) 433 node = node->document().ancestorInThisScope(node); 434 435 mutableRectBasedTestResult().add(node); 436 437 bool regionFilled = rect.contains(locationInContainer.boundingBox()); 438 return !regionFilled; 439 } 440 441 void HitTestResult::append(const HitTestResult& other) 442 { 443 ASSERT(isRectBasedTest() && other.isRectBasedTest()); 444 445 if (!m_scrollbar && other.scrollbar()) { 446 setScrollbar(other.scrollbar()); 447 } 448 449 if (!m_innerNode && other.innerNode()) { 450 m_innerNode = other.innerNode(); 451 m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode(); 452 m_innerNonSharedNode = other.innerNonSharedNode(); 453 m_localPoint = other.localPoint(); 454 m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; 455 m_innerURLElement = other.URLElement(); 456 m_isOverWidget = other.isOverWidget(); 457 } 458 459 if (other.m_rectBasedTestResult) { 460 NodeSet& set = mutableRectBasedTestResult(); 461 for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it) 462 set.add(it->get()); 463 } 464 } 465 466 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const 467 { 468 if (!m_rectBasedTestResult) 469 m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet); 470 return *m_rectBasedTestResult; 471 } 472 473 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult() 474 { 475 if (!m_rectBasedTestResult) 476 m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet); 477 return *m_rectBasedTestResult; 478 } 479 480 Node* HitTestResult::targetNode() const 481 { 482 Node* node = innerNode(); 483 if (!node) 484 return 0; 485 if (node->inDocument()) 486 return node; 487 488 Element* element = node->parentElement(); 489 if (element && element->inDocument()) 490 return element; 491 492 return node; 493 } 494 495 Element* HitTestResult::innerElement() const 496 { 497 for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) { 498 if (node->isElementNode()) 499 return toElement(node); 500 } 501 502 return 0; 503 } 504 505 } // namespace WebCore 506