1 /*
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21 #include "config.h"
22 #include "HitTestResult.h"
23
24 #include "Frame.h"
25 #include "FrameTree.h"
26 #include "HTMLAnchorElement.h"
27 #include "HTMLImageElement.h"
28 #include "HTMLInputElement.h"
29 #include "HTMLMediaElement.h"
30 #include "HTMLNames.h"
31 #include "RenderImage.h"
32 #include "Scrollbar.h"
33 #include "SelectionController.h"
34
35 #if ENABLE(SVG)
36 #include "SVGNames.h"
37 #include "XLinkNames.h"
38 #endif
39
40 #if ENABLE(WML)
41 #include "WMLImageElement.h"
42 #include "WMLNames.h"
43 #endif
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
HitTestResult(const IntPoint & point)49 HitTestResult::HitTestResult(const IntPoint& point)
50 : m_point(point)
51 , m_isOverWidget(false)
52 {
53 }
54
HitTestResult(const HitTestResult & other)55 HitTestResult::HitTestResult(const HitTestResult& other)
56 : m_innerNode(other.innerNode())
57 , m_innerNonSharedNode(other.innerNonSharedNode())
58 , m_point(other.point())
59 , m_localPoint(other.localPoint())
60 , m_innerURLElement(other.URLElement())
61 , m_scrollbar(other.scrollbar())
62 , m_isOverWidget(other.isOverWidget())
63 {
64 }
65
~HitTestResult()66 HitTestResult::~HitTestResult()
67 {
68 }
69
operator =(const HitTestResult & other)70 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
71 {
72 m_innerNode = other.innerNode();
73 m_innerNonSharedNode = other.innerNonSharedNode();
74 m_point = other.point();
75 m_localPoint = other.localPoint();
76 m_innerURLElement = other.URLElement();
77 m_scrollbar = other.scrollbar();
78 m_isOverWidget = other.isOverWidget();
79 return *this;
80 }
81
setToNonShadowAncestor()82 void HitTestResult::setToNonShadowAncestor()
83 {
84 Node* node = innerNode();
85 if (node)
86 node = node->shadowAncestorNode();
87 setInnerNode(node);
88 node = innerNonSharedNode();
89 if (node)
90 node = node->shadowAncestorNode();
91 setInnerNonSharedNode(node);
92 }
93
setInnerNode(Node * n)94 void HitTestResult::setInnerNode(Node* n)
95 {
96 m_innerNode = n;
97 }
98
setInnerNonSharedNode(Node * n)99 void HitTestResult::setInnerNonSharedNode(Node* n)
100 {
101 m_innerNonSharedNode = n;
102 }
103
setURLElement(Element * n)104 void HitTestResult::setURLElement(Element* n)
105 {
106 m_innerURLElement = n;
107 }
108
setScrollbar(Scrollbar * s)109 void HitTestResult::setScrollbar(Scrollbar* s)
110 {
111 m_scrollbar = s;
112 }
113
targetFrame() const114 Frame* HitTestResult::targetFrame() const
115 {
116 if (!m_innerURLElement)
117 return 0;
118
119 Frame* frame = m_innerURLElement->document()->frame();
120 if (!frame)
121 return 0;
122
123 return frame->tree()->find(m_innerURLElement->target());
124 }
125
isSelected() const126 bool HitTestResult::isSelected() const
127 {
128 if (!m_innerNonSharedNode)
129 return false;
130
131 Frame* frame = m_innerNonSharedNode->document()->frame();
132 if (!frame)
133 return false;
134
135 return frame->selection()->contains(m_point);
136 }
137
spellingToolTip(TextDirection & dir) const138 String HitTestResult::spellingToolTip(TextDirection& dir) const
139 {
140 dir = LTR;
141 // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
142 // currently supply strings, but maybe someday markers associated with misspelled words will also.
143 if (!m_innerNonSharedNode)
144 return String();
145
146 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Grammar);
147 if (!marker)
148 return String();
149
150 if (RenderObject* renderer = m_innerNonSharedNode->renderer())
151 dir = renderer->style()->direction();
152 return marker->description;
153 }
154
replacedString() const155 String HitTestResult::replacedString() const
156 {
157 // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected,
158 // and is used for generating a contextual menu item that allows it to easily be changed back if desired.
159 if (!m_innerNonSharedNode)
160 return String();
161
162 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Replacement);
163 if (!marker)
164 return String();
165
166 return marker->description;
167 }
168
title(TextDirection & dir) const169 String HitTestResult::title(TextDirection& dir) const
170 {
171 dir = LTR;
172 // Find the title in the nearest enclosing DOM node.
173 // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
174 for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
175 if (titleNode->isElementNode()) {
176 String title = static_cast<Element*>(titleNode)->title();
177 if (!title.isEmpty()) {
178 if (RenderObject* renderer = titleNode->renderer())
179 dir = renderer->style()->direction();
180 return title;
181 }
182 }
183 }
184 return String();
185 }
186
displayString(const String & string,const Node * node)187 String displayString(const String& string, const Node* node)
188 {
189 if (!node)
190 return string;
191 return node->document()->displayStringModifiedByEncoding(string);
192 }
193
altDisplayString() const194 String HitTestResult::altDisplayString() const
195 {
196 if (!m_innerNonSharedNode)
197 return String();
198
199 if (m_innerNonSharedNode->hasTagName(imgTag)) {
200 HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get());
201 return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
202 }
203
204 if (m_innerNonSharedNode->hasTagName(inputTag)) {
205 HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
206 return displayString(input->alt(), m_innerNonSharedNode.get());
207 }
208
209 #if ENABLE(WML)
210 if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) {
211 WMLImageElement* image = static_cast<WMLImageElement*>(m_innerNonSharedNode.get());
212 return displayString(image->altText(), m_innerNonSharedNode.get());
213 }
214 #endif
215
216 return String();
217 }
218
image() const219 Image* HitTestResult::image() const
220 {
221 if (!m_innerNonSharedNode)
222 return 0;
223
224 RenderObject* renderer = m_innerNonSharedNode->renderer();
225 if (renderer && renderer->isImage()) {
226 RenderImage* image = static_cast<WebCore::RenderImage*>(renderer);
227 if (image->cachedImage() && !image->cachedImage()->errorOccurred())
228 return image->cachedImage()->image();
229 }
230
231 return 0;
232 }
233
imageRect() const234 IntRect HitTestResult::imageRect() const
235 {
236 if (!image())
237 return IntRect();
238 return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
239 }
240
absoluteImageURL() const241 KURL HitTestResult::absoluteImageURL() const
242 {
243 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
244 return KURL();
245
246 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
247 return KURL();
248
249 AtomicString urlString;
250 if (m_innerNonSharedNode->hasTagName(embedTag)
251 || m_innerNonSharedNode->hasTagName(imgTag)
252 || m_innerNonSharedNode->hasTagName(inputTag)
253 || m_innerNonSharedNode->hasTagName(objectTag)
254 #if ENABLE(SVG)
255 || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
256 #endif
257 #if ENABLE(WML)
258 || m_innerNonSharedNode->hasTagName(WMLNames::imgTag)
259 #endif
260 ) {
261 Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
262 urlString = element->getAttribute(element->imageSourceAttributeName());
263 } else
264 return KURL();
265
266 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString));
267 }
268
absoluteMediaURL() const269 KURL HitTestResult::absoluteMediaURL() const
270 {
271 #if ENABLE(VIDEO)
272 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
273 return KURL();
274
275 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
276 return KURL();
277
278 AtomicString urlString;
279 if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag)) {
280 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
281 urlString = mediaElement->currentSrc();
282 } else
283 return KURL();
284
285 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString));
286 #else
287 return KURL();
288 #endif
289 }
290
absoluteLinkURL() const291 KURL HitTestResult::absoluteLinkURL() const
292 {
293 if (!(m_innerURLElement && m_innerURLElement->document()))
294 return KURL();
295
296 AtomicString urlString;
297 if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag))
298 urlString = m_innerURLElement->getAttribute(hrefAttr);
299 #if ENABLE(SVG)
300 else if (m_innerURLElement->hasTagName(SVGNames::aTag))
301 urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
302 #endif
303 #if ENABLE(WML)
304 else if (m_innerURLElement->hasTagName(WMLNames::aTag))
305 urlString = m_innerURLElement->getAttribute(hrefAttr);
306 #endif
307 else
308 return KURL();
309
310 return m_innerURLElement->document()->completeURL(deprecatedParseURL(urlString));
311 }
312
isLiveLink() const313 bool HitTestResult::isLiveLink() const
314 {
315 if (!(m_innerURLElement && m_innerURLElement->document()))
316 return false;
317
318 if (m_innerURLElement->hasTagName(aTag))
319 return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
320 #if ENABLE(SVG)
321 if (m_innerURLElement->hasTagName(SVGNames::aTag))
322 return m_innerURLElement->isLink();
323 #endif
324 #if ENABLE(WML)
325 if (m_innerURLElement->hasTagName(WMLNames::aTag))
326 return m_innerURLElement->isLink();
327 #endif
328
329 return false;
330 }
331
titleDisplayString() const332 String HitTestResult::titleDisplayString() const
333 {
334 if (!m_innerURLElement)
335 return String();
336
337 return displayString(m_innerURLElement->title(), m_innerURLElement.get());
338 }
339
textContent() const340 String HitTestResult::textContent() const
341 {
342 if (!m_innerURLElement)
343 return String();
344 return m_innerURLElement->textContent();
345 }
346
347 // FIXME: This function needs a better name and may belong in a different class. It's not
348 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
349 // function would make more sense in the ContextMenu class, except that WebElementDictionary
350 // hooks into it. Anyway, we should architect this better.
isContentEditable() const351 bool HitTestResult::isContentEditable() const
352 {
353 if (!m_innerNonSharedNode)
354 return false;
355
356 if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag))
357 return true;
358
359 if (m_innerNonSharedNode->hasTagName(inputTag))
360 return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
361
362 return m_innerNonSharedNode->isContentEditable();
363 }
364
365 } // namespace WebCore
366