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
boundingBox() const126 IntRect HitTestResult::boundingBox() const
127 {
128 if (m_innerNonSharedNode) {
129 RenderObject* renderer = m_innerNonSharedNode->renderer();
130 if (renderer)
131 return renderer->absoluteBoundingBoxRect();
132 }
133
134 return IntRect();
135 }
136
isSelected() const137 bool HitTestResult::isSelected() const
138 {
139 if (!m_innerNonSharedNode)
140 return false;
141
142 Frame* frame = m_innerNonSharedNode->document()->frame();
143 if (!frame)
144 return false;
145
146 return frame->selection()->contains(m_point);
147 }
148
spellingToolTip(TextDirection & dir) const149 String HitTestResult::spellingToolTip(TextDirection& dir) const
150 {
151 dir = LTR;
152 // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
153 // currently supply strings, but maybe someday markers associated with misspelled words will also.
154 if (!m_innerNonSharedNode)
155 return String();
156
157 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Grammar);
158 if (!marker)
159 return String();
160
161 if (RenderObject* renderer = m_innerNonSharedNode->renderer())
162 dir = renderer->style()->direction();
163 return marker->description;
164 }
165
replacedString() const166 String HitTestResult::replacedString() const
167 {
168 // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected,
169 // and is used for generating a contextual menu item that allows it to easily be changed back if desired.
170 if (!m_innerNonSharedNode)
171 return String();
172
173 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Replacement);
174 if (!marker)
175 return String();
176
177 return marker->description;
178 }
179
title(TextDirection & dir) const180 String HitTestResult::title(TextDirection& dir) const
181 {
182 dir = LTR;
183 // Find the title in the nearest enclosing DOM node.
184 // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
185 for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
186 if (titleNode->isElementNode()) {
187 String title = static_cast<Element*>(titleNode)->title();
188 if (!title.isEmpty()) {
189 if (RenderObject* renderer = titleNode->renderer())
190 dir = renderer->style()->direction();
191 return title;
192 }
193 }
194 }
195 return String();
196 }
197
displayString(const String & string,const Node * node)198 String displayString(const String& string, const Node* node)
199 {
200 if (!node)
201 return string;
202 return node->document()->displayStringModifiedByEncoding(string);
203 }
204
altDisplayString() const205 String HitTestResult::altDisplayString() const
206 {
207 if (!m_innerNonSharedNode)
208 return String();
209
210 if (m_innerNonSharedNode->hasTagName(imgTag)) {
211 HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get());
212 return displayString(image->alt(), m_innerNonSharedNode.get());
213 }
214
215 if (m_innerNonSharedNode->hasTagName(inputTag)) {
216 HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
217 return displayString(input->alt(), m_innerNonSharedNode.get());
218 }
219
220 #if ENABLE(WML)
221 if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) {
222 WMLImageElement* image = static_cast<WMLImageElement*>(m_innerNonSharedNode.get());
223 return displayString(image->altText(), m_innerNonSharedNode.get());
224 }
225 #endif
226
227 return String();
228 }
229
image() const230 Image* HitTestResult::image() const
231 {
232 if (!m_innerNonSharedNode)
233 return 0;
234
235 RenderObject* renderer = m_innerNonSharedNode->renderer();
236 if (renderer && renderer->isImage()) {
237 RenderImage* image = static_cast<WebCore::RenderImage*>(renderer);
238 if (image->cachedImage() && !image->cachedImage()->errorOccurred())
239 return image->cachedImage()->image();
240 }
241
242 return 0;
243 }
244
imageRect() const245 IntRect HitTestResult::imageRect() const
246 {
247 if (!image())
248 return IntRect();
249 return m_innerNonSharedNode->renderBox()->absoluteContentBox();
250 }
251
absoluteImageURL() const252 KURL HitTestResult::absoluteImageURL() const
253 {
254 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
255 return KURL();
256
257 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
258 return KURL();
259
260 AtomicString urlString;
261 if (m_innerNonSharedNode->hasTagName(embedTag)
262 || m_innerNonSharedNode->hasTagName(imgTag)
263 || m_innerNonSharedNode->hasTagName(inputTag)
264 || m_innerNonSharedNode->hasTagName(objectTag)
265 #if ENABLE(SVG)
266 || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
267 #endif
268 #if ENABLE(WML)
269 || m_innerNonSharedNode->hasTagName(WMLNames::imgTag)
270 #endif
271 ) {
272 Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
273 urlString = element->getAttribute(element->imageSourceAttributeName());
274 } else
275 return KURL();
276
277 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString));
278 }
279
absoluteMediaURL() const280 KURL HitTestResult::absoluteMediaURL() const
281 {
282 #if ENABLE(VIDEO)
283 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
284 return KURL();
285
286 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
287 return KURL();
288
289 AtomicString urlString;
290 if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag)) {
291 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
292 urlString = mediaElement->currentSrc();
293 } else
294 return KURL();
295
296 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString));
297 #else
298 return KURL();
299 #endif
300 }
301
absoluteLinkURL() const302 KURL HitTestResult::absoluteLinkURL() const
303 {
304 if (!(m_innerURLElement && m_innerURLElement->document()))
305 return KURL();
306
307 AtomicString urlString;
308 if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag))
309 urlString = m_innerURLElement->getAttribute(hrefAttr);
310 #if ENABLE(SVG)
311 else if (m_innerURLElement->hasTagName(SVGNames::aTag))
312 urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
313 #endif
314 #if ENABLE(WML)
315 else if (m_innerURLElement->hasTagName(WMLNames::aTag))
316 urlString = m_innerURLElement->getAttribute(hrefAttr);
317 #endif
318 else
319 return KURL();
320
321 return m_innerURLElement->document()->completeURL(deprecatedParseURL(urlString));
322 }
323
isLiveLink() const324 bool HitTestResult::isLiveLink() const
325 {
326 if (!(m_innerURLElement && m_innerURLElement->document()))
327 return false;
328
329 if (m_innerURLElement->hasTagName(aTag))
330 return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
331 #if ENABLE(SVG)
332 if (m_innerURLElement->hasTagName(SVGNames::aTag))
333 return m_innerURLElement->isLink();
334 #endif
335 #if ENABLE(WML)
336 if (m_innerURLElement->hasTagName(WMLNames::aTag))
337 return m_innerURLElement->isLink();
338 #endif
339
340 return false;
341 }
342
titleDisplayString() const343 String HitTestResult::titleDisplayString() const
344 {
345 if (!m_innerURLElement)
346 return String();
347
348 return displayString(m_innerURLElement->title(), m_innerURLElement.get());
349 }
350
textContent() const351 String HitTestResult::textContent() const
352 {
353 if (!m_innerURLElement)
354 return String();
355 return m_innerURLElement->textContent();
356 }
357
358 // FIXME: This function needs a better name and may belong in a different class. It's not
359 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
360 // function would make more sense in the ContextMenu class, except that WebElementDictionary
361 // hooks into it. Anyway, we should architect this better.
isContentEditable() const362 bool HitTestResult::isContentEditable() const
363 {
364 if (!m_innerNonSharedNode)
365 return false;
366
367 if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag))
368 return true;
369
370 if (m_innerNonSharedNode->hasTagName(inputTag))
371 return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
372
373 return m_innerNonSharedNode->isContentEditable();
374 }
375
376 } // namespace WebCore
377