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
43namespace blink {
44
45using namespace HTMLNames;
46
47HitTestResult::HitTestResult()
48    : m_isOverWidget(false)
49    , m_isFirstLetter(false)
50{
51}
52
53HitTestResult::HitTestResult(const LayoutPoint& point)
54    : m_hitTestLocation(point)
55    , m_pointInInnerNodeFrame(point)
56    , m_isOverWidget(false)
57    , m_isFirstLetter(false)
58{
59}
60
61HitTestResult::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
69HitTestResult::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
77HitTestResult::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
93HitTestResult::~HitTestResult()
94{
95}
96
97HitTestResult& 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
116void 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
127PositionWithAffinity 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
139RenderObject* 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
149void 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
166void HitTestResult::setInnerNode(Node* n)
167{
168    m_innerPossiblyPseudoNode = n;
169    if (n && n->isPseudoElement())
170        n = n->parentOrShadowHostNode();
171    m_innerNode = n;
172}
173
174void HitTestResult::setInnerNonSharedNode(Node* n)
175{
176    if (n && n->isPseudoElement())
177        n = n->parentOrShadowHostNode();
178    m_innerNonSharedNode = n;
179}
180
181void HitTestResult::setURLElement(Element* n)
182{
183    m_innerURLElement = n;
184}
185
186void HitTestResult::setScrollbar(Scrollbar* s)
187{
188    m_scrollbar = s;
189}
190
191LocalFrame* 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
200bool 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
210String 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
227String 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
245const 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
263Image* 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
278IntRect HitTestResult::imageRect() const
279{
280    if (!image())
281        return IntRect();
282    return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
283}
284
285KURL 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
308KURL HitTestResult::absoluteMediaURL() const
309{
310    if (HTMLMediaElement* mediaElt = mediaElement())
311        return mediaElt->currentSrc();
312    return KURL();
313}
314
315HTMLMediaElement* 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
328KURL HitTestResult::absoluteLinkURL() const
329{
330    if (!m_innerURLElement)
331        return KURL();
332    return m_innerURLElement->hrefURL();
333}
334
335bool HitTestResult::isLiveLink() const
336{
337    return m_innerURLElement && m_innerURLElement->isLiveLink();
338}
339
340bool 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
351bool HitTestResult::isOverLink() const
352{
353    return m_innerURLElement && m_innerURLElement->isLink();
354}
355
356String 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.
367bool 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
381bool 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
398bool 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
415void 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
440const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
441{
442    if (!m_rectBasedTestResult)
443        m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
444    return *m_rectBasedTestResult;
445}
446
447HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
448{
449    if (!m_rectBasedTestResult)
450        m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
451    return *m_rectBasedTestResult;
452}
453
454void 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
472Element* 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