summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/HitTestResult.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/rendering/HitTestResult.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/rendering/HitTestResult.cpp')
-rw-r--r--Source/WebCore/rendering/HitTestResult.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/HitTestResult.cpp b/Source/WebCore/rendering/HitTestResult.cpp
new file mode 100644
index 0000000..5d9b3df
--- /dev/null
+++ b/Source/WebCore/rendering/HitTestResult.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+*/
+
+#include "config.h"
+#include "HitTestResult.h"
+
+#include "DocumentMarkerController.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLVideoElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
+#include "RenderImage.h"
+#include "Scrollbar.h"
+#include "SelectionController.h"
+
+#if ENABLE(SVG)
+#include "SVGNames.h"
+#include "XLinkNames.h"
+#endif
+
+#if ENABLE(WML)
+#include "WMLImageElement.h"
+#include "WMLNames.h"
+#endif
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+HitTestResult::HitTestResult()
+ : m_isOverWidget(false)
+ , m_isRectBased(false)
+ , m_topPadding(0)
+ , m_rightPadding(0)
+ , m_bottomPadding(0)
+ , m_leftPadding(0)
+{
+}
+
+HitTestResult::HitTestResult(const IntPoint& point)
+ : m_point(point)
+ , m_isOverWidget(false)
+ , m_isRectBased(false)
+ , m_topPadding(0)
+ , m_rightPadding(0)
+ , m_bottomPadding(0)
+ , m_leftPadding(0)
+{
+}
+
+HitTestResult::HitTestResult(const IntPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
+ : m_point(centerPoint)
+ , m_isOverWidget(false)
+ , m_topPadding(topPadding)
+ , m_rightPadding(rightPadding)
+ , m_bottomPadding(bottomPadding)
+ , m_leftPadding(leftPadding)
+{
+ // If all padding values passed in are zero then it is not a rect based hit test.
+ m_isRectBased = topPadding || rightPadding || bottomPadding || leftPadding;
+
+ // Make sure all padding values are clamped to zero if it is not a rect hit test.
+ if (!m_isRectBased)
+ m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
+}
+
+HitTestResult::HitTestResult(const HitTestResult& other)
+ : m_innerNode(other.innerNode())
+ , m_innerNonSharedNode(other.innerNonSharedNode())
+ , m_point(other.point())
+ , m_localPoint(other.localPoint())
+ , m_innerURLElement(other.URLElement())
+ , m_scrollbar(other.scrollbar())
+ , m_isOverWidget(other.isOverWidget())
+{
+ // Only copy the padding and ListHashSet in case of rect hit test.
+ // Copying the later is rather expensive.
+ if ((m_isRectBased = other.isRectBasedTest())) {
+ m_topPadding = other.m_topPadding;
+ m_rightPadding = other.m_rightPadding;
+ m_bottomPadding = other.m_bottomPadding;
+ m_leftPadding = other.m_leftPadding;
+ m_rectBasedTestResult = other.rectBasedTestResult();
+ } else
+ m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
+}
+
+HitTestResult::~HitTestResult()
+{
+}
+
+HitTestResult& HitTestResult::operator=(const HitTestResult& other)
+{
+ m_innerNode = other.innerNode();
+ m_innerNonSharedNode = other.innerNonSharedNode();
+ m_point = other.point();
+ m_localPoint = other.localPoint();
+ m_innerURLElement = other.URLElement();
+ m_scrollbar = other.scrollbar();
+ m_isOverWidget = other.isOverWidget();
+ // Only copy the padding and ListHashSet in case of rect hit test.
+ // Copying the later is rather expensive.
+ if ((m_isRectBased = other.isRectBasedTest())) {
+ m_topPadding = other.m_topPadding;
+ m_rightPadding = other.m_rightPadding;
+ m_bottomPadding = other.m_bottomPadding;
+ m_leftPadding = other.m_leftPadding;
+ m_rectBasedTestResult = other.rectBasedTestResult();
+ } else
+ m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
+ return *this;
+}
+
+void HitTestResult::setToNonShadowAncestor()
+{
+ Node* node = innerNode();
+ if (node)
+ node = node->shadowAncestorNode();
+ setInnerNode(node);
+ node = innerNonSharedNode();
+ if (node)
+ node = node->shadowAncestorNode();
+ setInnerNonSharedNode(node);
+}
+
+void HitTestResult::setInnerNode(Node* n)
+{
+ m_innerNode = n;
+}
+
+void HitTestResult::setInnerNonSharedNode(Node* n)
+{
+ m_innerNonSharedNode = n;
+}
+
+void HitTestResult::setURLElement(Element* n)
+{
+ m_innerURLElement = n;
+}
+
+void HitTestResult::setScrollbar(Scrollbar* s)
+{
+ m_scrollbar = s;
+}
+
+Frame* HitTestResult::targetFrame() const
+{
+ if (!m_innerURLElement)
+ return 0;
+
+ Frame* frame = m_innerURLElement->document()->frame();
+ if (!frame)
+ return 0;
+
+ return frame->tree()->find(m_innerURLElement->target());
+}
+
+bool HitTestResult::isSelected() const
+{
+ if (!m_innerNonSharedNode)
+ return false;
+
+ Frame* frame = m_innerNonSharedNode->document()->frame();
+ if (!frame)
+ return false;
+
+ return frame->selection()->contains(m_point);
+}
+
+String HitTestResult::spellingToolTip(TextDirection& dir) const
+{
+ dir = LTR;
+ // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
+ // currently supply strings, but maybe someday markers associated with misspelled words will also.
+ if (!m_innerNonSharedNode)
+ return String();
+
+ DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Grammar);
+ if (!marker)
+ return String();
+
+ if (RenderObject* renderer = m_innerNonSharedNode->renderer())
+ dir = renderer->style()->direction();
+ return marker->description;
+}
+
+String HitTestResult::replacedString() const
+{
+ // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected,
+ // and is used for generating a contextual menu item that allows it to easily be changed back if desired.
+ if (!m_innerNonSharedNode)
+ return String();
+
+ DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Replacement);
+ if (!marker)
+ return String();
+
+ return marker->description;
+}
+
+String HitTestResult::title(TextDirection& dir) const
+{
+ dir = LTR;
+ // Find the title in the nearest enclosing DOM node.
+ // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
+ for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
+ if (titleNode->isElementNode()) {
+ String title = static_cast<Element*>(titleNode)->title();
+ if (!title.isEmpty()) {
+ if (RenderObject* renderer = titleNode->renderer())
+ dir = renderer->style()->direction();
+ return title;
+ }
+ }
+ }
+ return String();
+}
+
+String displayString(const String& string, const Node* node)
+{
+ if (!node)
+ return string;
+ return node->document()->displayStringModifiedByEncoding(string);
+}
+
+String HitTestResult::altDisplayString() const
+{
+ if (!m_innerNonSharedNode)
+ return String();
+
+ if (m_innerNonSharedNode->hasTagName(imgTag)) {
+ HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get());
+ return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
+ }
+
+ if (m_innerNonSharedNode->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
+ return displayString(input->alt(), m_innerNonSharedNode.get());
+ }
+
+#if ENABLE(WML)
+ if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) {
+ WMLImageElement* image = static_cast<WMLImageElement*>(m_innerNonSharedNode.get());
+ return displayString(image->altText(), m_innerNonSharedNode.get());
+ }
+#endif
+
+ return String();
+}
+
+Image* HitTestResult::image() const
+{
+ if (!m_innerNonSharedNode)
+ return 0;
+
+ RenderObject* renderer = m_innerNonSharedNode->renderer();
+ if (renderer && renderer->isImage()) {
+ RenderImage* image = static_cast<WebCore::RenderImage*>(renderer);
+ if (image->cachedImage() && !image->cachedImage()->errorOccurred())
+ return image->cachedImage()->image();
+ }
+
+ return 0;
+}
+
+IntRect HitTestResult::imageRect() const
+{
+ if (!image())
+ return IntRect();
+ return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
+}
+
+KURL HitTestResult::absoluteImageURL() const
+{
+ if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
+ return KURL();
+
+ if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
+ return KURL();
+
+ AtomicString urlString;
+ if (m_innerNonSharedNode->hasTagName(embedTag)
+ || m_innerNonSharedNode->hasTagName(imgTag)
+ || m_innerNonSharedNode->hasTagName(inputTag)
+ || m_innerNonSharedNode->hasTagName(objectTag)
+#if ENABLE(SVG)
+ || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
+#endif
+#if ENABLE(WML)
+ || m_innerNonSharedNode->hasTagName(WMLNames::imgTag)
+#endif
+ ) {
+ Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
+ urlString = element->getAttribute(element->imageSourceAttributeName());
+ } else
+ return KURL();
+
+ return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
+}
+
+KURL HitTestResult::absoluteMediaURL() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(mediaElt->currentSrc()));
+ return KURL();
+#else
+ return KURL();
+#endif
+}
+
+bool HitTestResult::mediaSupportsFullscreen() const
+{
+#if ENABLE(VIDEO)
+ HTMLMediaElement* mediaElt(mediaElement());
+ return (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag) && mediaElt->supportsFullscreen());
+#else
+ return false;
+#endif
+}
+
+#if ENABLE(VIDEO)
+HTMLMediaElement* HitTestResult::mediaElement() const
+{
+ if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
+ return 0;
+
+ if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
+ return 0;
+
+ if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
+ return static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
+ return 0;
+}
+#endif
+
+void HitTestResult::toggleMediaControlsDisplay() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ mediaElt->setControls(!mediaElt->controls());
+#endif
+}
+
+void HitTestResult::toggleMediaLoopPlayback() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ mediaElt->setLoop(!mediaElt->loop());
+#endif
+}
+
+void HitTestResult::enterFullscreenForVideo() const
+{
+#if ENABLE(VIDEO)
+ HTMLMediaElement* mediaElt(mediaElement());
+ if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) {
+ HTMLVideoElement* videoElt = static_cast<HTMLVideoElement*>(mediaElt);
+ if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen())
+ videoElt->enterFullscreen();
+ }
+#endif
+}
+
+bool HitTestResult::mediaControlsEnabled() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->controls();
+#endif
+ return false;
+}
+
+bool HitTestResult::mediaLoopEnabled() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->loop();
+#endif
+ return false;
+}
+
+bool HitTestResult::mediaPlaying() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return !mediaElt->paused();
+#endif
+ return false;
+}
+
+void HitTestResult::toggleMediaPlayState() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ mediaElt->togglePlayState();
+#endif
+}
+
+bool HitTestResult::mediaHasAudio() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->hasAudio();
+#endif
+ return false;
+}
+
+bool HitTestResult::mediaIsVideo() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->hasTagName(HTMLNames::videoTag);
+#endif
+ return false;
+}
+
+bool HitTestResult::mediaMuted() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->muted();
+#endif
+ return false;
+}
+
+void HitTestResult::toggleMediaMuteState() const
+{
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ mediaElt->setMuted(!mediaElt->muted());
+#endif
+}
+
+KURL HitTestResult::absoluteLinkURL() const
+{
+ if (!(m_innerURLElement && m_innerURLElement->document()))
+ return KURL();
+
+ AtomicString urlString;
+ if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag))
+ urlString = m_innerURLElement->getAttribute(hrefAttr);
+#if ENABLE(SVG)
+ else if (m_innerURLElement->hasTagName(SVGNames::aTag))
+ urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
+#endif
+#if ENABLE(WML)
+ else if (m_innerURLElement->hasTagName(WMLNames::aTag))
+ urlString = m_innerURLElement->getAttribute(hrefAttr);
+#endif
+ else
+ return KURL();
+
+ return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
+}
+
+bool HitTestResult::isLiveLink() const
+{
+ if (!(m_innerURLElement && m_innerURLElement->document()))
+ return false;
+
+ if (m_innerURLElement->hasTagName(aTag))
+ return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
+#if ENABLE(SVG)
+ if (m_innerURLElement->hasTagName(SVGNames::aTag))
+ return m_innerURLElement->isLink();
+#endif
+#if ENABLE(WML)
+ if (m_innerURLElement->hasTagName(WMLNames::aTag))
+ return m_innerURLElement->isLink();
+#endif
+
+ return false;
+}
+
+String HitTestResult::titleDisplayString() const
+{
+ if (!m_innerURLElement)
+ return String();
+
+ return displayString(m_innerURLElement->title(), m_innerURLElement.get());
+}
+
+String HitTestResult::textContent() const
+{
+ if (!m_innerURLElement)
+ return String();
+ return m_innerURLElement->textContent();
+}
+
+// FIXME: This function needs a better name and may belong in a different class. It's not
+// really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
+// function would make more sense in the ContextMenu class, except that WebElementDictionary
+// hooks into it. Anyway, we should architect this better.
+bool HitTestResult::isContentEditable() const
+{
+ if (!m_innerNonSharedNode)
+ return false;
+
+ if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag))
+ return true;
+
+ if (m_innerNonSharedNode->hasTagName(inputTag))
+ return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
+
+ return m_innerNonSharedNode->isContentEditable();
+}
+
+bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const IntRect& rect)
+{
+ // If it is not a rect-based hit test, this method has to be no-op.
+ // Return false, so the hit test stops.
+ if (!isRectBasedTest())
+ return false;
+
+ // If node is null, return true so the hit test can continue.
+ if (!node)
+ return true;
+
+ node = node->shadowAncestorNode();
+ m_rectBasedTestResult.add(node);
+
+ return !rect.contains(rectForPoint(x, y));
+}
+
+void HitTestResult::append(const HitTestResult& other)
+{
+ ASSERT(isRectBasedTest() && other.isRectBasedTest());
+
+ if (!m_innerNode && other.innerNode()) {
+ m_innerNode = other.innerNode();
+ m_innerNonSharedNode = other.innerNonSharedNode();
+ m_localPoint = other.localPoint();
+ m_innerURLElement = other.URLElement();
+ m_scrollbar = other.scrollbar();
+ m_isOverWidget = other.isOverWidget();
+ }
+
+ const ListHashSet<RefPtr<Node> >& list = other.rectBasedTestResult();
+ ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
+ for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it)
+ m_rectBasedTestResult.add(it->get());
+}
+
+IntRect HitTestResult::rectForPoint(const IntPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
+{
+ IntPoint actualPoint(point);
+ actualPoint -= IntSize(leftPadding, topPadding);
+
+ IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding);
+ // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1".
+ actualPadding += IntSize(1, 1);
+
+ return IntRect(actualPoint, actualPadding);
+}
+
+} // namespace WebCore