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