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 "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