Home | History | Annotate | Download | only in rendering
      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