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