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