initial import
[vuplus_webkit] / Source / WebCore / rendering / HitTestResult.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2011 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 "DocumentMarkerController.h"
25 #include "Frame.h"
26 #include "FrameSelection.h"
27 #include "FrameTree.h"
28 #include "HTMLAnchorElement.h"
29 #include "HTMLVideoElement.h"
30 #include "HTMLImageElement.h"
31 #include "HTMLInputElement.h"
32 #include "HTMLMediaElement.h"
33 #include "HTMLNames.h"
34 #include "HTMLParserIdioms.h"
35 #include "RenderBlock.h"
36 #include "RenderImage.h"
37 #include "RenderInline.h"
38 #include "Scrollbar.h"
39
40 #if ENABLE(SVG)
41 #include "SVGNames.h"
42 #include "XLinkNames.h"
43 #endif
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 HitTestResult::HitTestResult()
50     : m_isOverWidget(false)
51     , m_isRectBased(false)
52     , m_topPadding(0)
53     , m_rightPadding(0)
54     , m_bottomPadding(0)
55     , m_leftPadding(0)
56 {
57 }
58
59 HitTestResult::HitTestResult(const LayoutPoint& point)
60     : m_point(point)
61     , m_isOverWidget(false)
62     , m_isRectBased(false)
63     , m_topPadding(0)
64     , m_rightPadding(0)
65     , m_bottomPadding(0)
66     , m_leftPadding(0)
67 {
68 }
69
70 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
71     : m_point(centerPoint)
72     , m_isOverWidget(false)
73     , m_topPadding(topPadding)
74     , m_rightPadding(rightPadding)
75     , m_bottomPadding(bottomPadding)
76     , m_leftPadding(leftPadding)
77 {
78     // If all padding values passed in are zero then it is not a rect based hit test.
79     m_isRectBased = topPadding || rightPadding || bottomPadding || leftPadding;
80
81     // Make sure all padding values are clamped to zero if it is not a rect hit test.
82     if (!m_isRectBased)
83         m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
84 }
85
86 HitTestResult::HitTestResult(const HitTestResult& other)
87     : m_innerNode(other.innerNode())
88     , m_innerNonSharedNode(other.innerNonSharedNode())
89     , m_point(other.point())
90     , m_localPoint(other.localPoint())
91     , m_innerURLElement(other.URLElement())
92     , m_scrollbar(other.scrollbar())
93     , m_isOverWidget(other.isOverWidget())
94 {
95     // Only copy the padding and NodeSet in case of rect hit test.
96     // Copying the later is rather expensive.
97     if ((m_isRectBased = other.isRectBasedTest())) {
98         m_topPadding = other.m_topPadding;
99         m_rightPadding = other.m_rightPadding;
100         m_bottomPadding = other.m_bottomPadding;
101         m_leftPadding = other.m_leftPadding;
102     } else
103         m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
104
105     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
106 }
107
108 HitTestResult::~HitTestResult()
109 {
110 }
111
112 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
113 {
114     m_innerNode = other.innerNode();
115     m_innerNonSharedNode = other.innerNonSharedNode();
116     m_point = other.point();
117     m_localPoint = other.localPoint();
118     m_innerURLElement = other.URLElement();
119     m_scrollbar = other.scrollbar();
120     m_isOverWidget = other.isOverWidget();
121     // Only copy the padding and NodeSet in case of rect hit test.
122     // Copying the later is rather expensive.
123     if ((m_isRectBased = other.isRectBasedTest())) {
124         m_topPadding = other.m_topPadding;
125         m_rightPadding = other.m_rightPadding;
126         m_bottomPadding = other.m_bottomPadding;
127         m_leftPadding = other.m_leftPadding;
128     } else
129         m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0;
130
131     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
132     return *this;
133 }
134
135 void HitTestResult::setToNonShadowAncestor()
136 {
137     Node* node = innerNode();
138     if (node)
139         node = node->shadowAncestorNode();
140     setInnerNode(node);
141     node = innerNonSharedNode();
142     if (node)
143         node = node->shadowAncestorNode();
144     setInnerNonSharedNode(node);
145 }
146
147 void HitTestResult::setInnerNode(Node* n)
148 {
149     m_innerNode = n;
150 }
151     
152 void HitTestResult::setInnerNonSharedNode(Node* n)
153 {
154     m_innerNonSharedNode = n;
155 }
156
157 void HitTestResult::setURLElement(Element* n) 
158
159     m_innerURLElement = n; 
160 }
161
162 void HitTestResult::setScrollbar(Scrollbar* s)
163 {
164     m_scrollbar = s;
165 }
166
167 Frame* HitTestResult::targetFrame() const
168 {
169     if (!m_innerURLElement)
170         return 0;
171
172     Frame* frame = m_innerURLElement->document()->frame();
173     if (!frame)
174         return 0;
175
176     return frame->tree()->find(m_innerURLElement->target());
177 }
178
179 bool HitTestResult::isSelected() const
180 {
181     if (!m_innerNonSharedNode)
182         return false;
183
184     Frame* frame = m_innerNonSharedNode->document()->frame();
185     if (!frame)
186         return false;
187
188     return frame->selection()->contains(m_point);
189 }
190
191 String HitTestResult::spellingToolTip(TextDirection& dir) const
192 {
193     dir = LTR;
194     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
195     // currently supply strings, but maybe someday markers associated with misspelled words will also.
196     if (!m_innerNonSharedNode)
197         return String();
198     
199     DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Grammar);
200     if (!marker)
201         return String();
202
203     if (RenderObject* renderer = m_innerNonSharedNode->renderer())
204         dir = renderer->style()->direction();
205     return marker->description();
206 }
207
208 String HitTestResult::replacedString() const
209 {
210     // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected, 
211     // and is used for generating a contextual menu item that allows it to easily be changed back if desired.
212     if (!m_innerNonSharedNode)
213         return String();
214     
215     DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Replacement);
216     if (!marker)
217         return String();
218     
219     return marker->description();
220 }    
221     
222 String HitTestResult::title(TextDirection& dir) const
223 {
224     dir = LTR;
225     // Find the title in the nearest enclosing DOM node.
226     // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
227     for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
228         if (titleNode->isElementNode()) {
229             String title = static_cast<Element*>(titleNode)->title();
230             if (!title.isEmpty()) {
231                 if (RenderObject* renderer = titleNode->renderer())
232                     dir = renderer->style()->direction();
233                 return title;
234             }
235         }
236     }
237     return String();
238 }
239
240 String HitTestResult::innerTextIfTruncated(TextDirection& dir) const
241 {
242     for (Node* truncatedNode = m_innerNode.get(); truncatedNode; truncatedNode = truncatedNode->parentNode()) {
243         if (!truncatedNode->isElementNode())
244             continue;
245
246         if (RenderObject* renderer = truncatedNode->renderer()) {
247             if (renderer->isRenderBlock()) {
248                 RenderBlock* block = toRenderBlock(renderer);
249                 if (block->style()->textOverflow()) {
250                     for (RootInlineBox* line = block->firstRootBox(); line; line = line->nextRootBox()) {
251                         if (line->hasEllipsisBox()) {
252                             dir = block->style()->direction();
253                             return toElement(truncatedNode)->innerText();
254                         }
255                     }
256                 }
257                 break;
258             }
259         }
260     }
261
262     dir = LTR;
263     return String();
264 }
265
266 String displayString(const String& string, const Node* node)
267 {
268     if (!node)
269         return string;
270     return node->document()->displayStringModifiedByEncoding(string);
271 }
272
273 String HitTestResult::altDisplayString() const
274 {
275     if (!m_innerNonSharedNode)
276         return String();
277     
278     if (m_innerNonSharedNode->hasTagName(imgTag)) {
279         HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get());
280         return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
281     }
282     
283     if (m_innerNonSharedNode->hasTagName(inputTag)) {
284         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
285         return displayString(input->alt(), m_innerNonSharedNode.get());
286     }
287
288     return String();
289 }
290
291 Image* HitTestResult::image() const
292 {
293     if (!m_innerNonSharedNode)
294         return 0;
295     
296     RenderObject* renderer = m_innerNonSharedNode->renderer();
297     if (renderer && renderer->isImage()) {
298         RenderImage* image = static_cast<WebCore::RenderImage*>(renderer);
299         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
300             return image->cachedImage()->image();
301     }
302
303     return 0;
304 }
305
306 IntRect HitTestResult::imageRect() const
307 {
308     if (!image())
309         return IntRect();
310     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
311 }
312
313 KURL HitTestResult::absoluteImageURL() const
314 {
315     if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
316         return KURL();
317
318     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
319         return KURL();
320
321     AtomicString urlString;
322     if (m_innerNonSharedNode->hasTagName(embedTag)
323         || m_innerNonSharedNode->hasTagName(imgTag)
324         || m_innerNonSharedNode->hasTagName(inputTag)
325         || m_innerNonSharedNode->hasTagName(objectTag)    
326 #if ENABLE(SVG)
327         || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
328 #endif
329        ) {
330         Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
331         urlString = element->getAttribute(element->imageSourceAttributeName());
332     } else
333         return KURL();
334
335     return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
336 }
337
338 KURL HitTestResult::absoluteMediaURL() const
339 {
340 #if ENABLE(VIDEO)
341     if (HTMLMediaElement* mediaElt = mediaElement())
342         return mediaElt->currentSrc();
343     return KURL();
344 #else
345     return KURL();
346 #endif
347 }
348
349 bool HitTestResult::mediaSupportsFullscreen() const
350 {
351 #if ENABLE(VIDEO)
352     HTMLMediaElement* mediaElt(mediaElement());
353     return (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag) && mediaElt->supportsFullscreen());
354 #else
355     return false;
356 #endif
357 }
358
359 #if ENABLE(VIDEO)
360 HTMLMediaElement* HitTestResult::mediaElement() const
361 {
362     if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
363         return 0;
364
365     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
366         return 0;
367
368     if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
369         return static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
370     return 0;
371 }
372 #endif
373
374 void HitTestResult::toggleMediaControlsDisplay() const
375 {
376 #if ENABLE(VIDEO)
377     if (HTMLMediaElement* mediaElt = mediaElement())
378         mediaElt->setControls(!mediaElt->controls());
379 #endif
380 }
381
382 void HitTestResult::toggleMediaLoopPlayback() const
383 {
384 #if ENABLE(VIDEO)
385     if (HTMLMediaElement* mediaElt = mediaElement())
386         mediaElt->setLoop(!mediaElt->loop());
387 #endif
388 }
389
390 void HitTestResult::enterFullscreenForVideo() const
391 {
392 #if ENABLE(VIDEO)
393     HTMLMediaElement* mediaElt(mediaElement());
394     if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) {
395         HTMLVideoElement* videoElt = static_cast<HTMLVideoElement*>(mediaElt);
396         if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen())
397             videoElt->enterFullscreen();
398     }
399 #endif
400 }
401
402 bool HitTestResult::mediaControlsEnabled() const
403 {
404 #if ENABLE(VIDEO)
405     if (HTMLMediaElement* mediaElt = mediaElement())
406         return mediaElt->controls();
407 #endif
408     return false;
409 }
410
411 bool HitTestResult::mediaLoopEnabled() const
412 {
413 #if ENABLE(VIDEO)
414     if (HTMLMediaElement* mediaElt = mediaElement())
415         return mediaElt->loop();
416 #endif
417     return false;
418 }
419
420 bool HitTestResult::mediaPlaying() const
421 {
422 #if ENABLE(VIDEO)
423     if (HTMLMediaElement* mediaElt = mediaElement())
424         return !mediaElt->paused();
425 #endif
426     return false;
427 }
428
429 void HitTestResult::toggleMediaPlayState() const
430 {
431 #if ENABLE(VIDEO)
432     if (HTMLMediaElement* mediaElt = mediaElement())
433         mediaElt->togglePlayState();
434 #endif
435 }
436
437 bool HitTestResult::mediaHasAudio() const
438 {
439 #if ENABLE(VIDEO)
440     if (HTMLMediaElement* mediaElt = mediaElement())
441         return mediaElt->hasAudio();
442 #endif
443     return false;
444 }
445
446 bool HitTestResult::mediaIsVideo() const
447 {
448 #if ENABLE(VIDEO)
449     if (HTMLMediaElement* mediaElt = mediaElement())
450         return mediaElt->hasTagName(HTMLNames::videoTag);
451 #endif
452     return false;
453 }
454
455 bool HitTestResult::mediaMuted() const
456 {
457 #if ENABLE(VIDEO)
458     if (HTMLMediaElement* mediaElt = mediaElement())
459         return mediaElt->muted();
460 #endif
461     return false;
462 }
463
464 void HitTestResult::toggleMediaMuteState() const
465 {
466 #if ENABLE(VIDEO)
467     if (HTMLMediaElement* mediaElt = mediaElement())
468         mediaElt->setMuted(!mediaElt->muted());
469 #endif
470 }
471
472 KURL HitTestResult::absoluteLinkURL() const
473 {
474     if (!(m_innerURLElement && m_innerURLElement->document()))
475         return KURL();
476
477     AtomicString urlString;
478     if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag))
479         urlString = m_innerURLElement->getAttribute(hrefAttr);
480 #if ENABLE(SVG)
481     else if (m_innerURLElement->hasTagName(SVGNames::aTag))
482         urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
483 #endif
484     else
485         return KURL();
486
487     return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
488 }
489
490 bool HitTestResult::isLiveLink() const
491 {
492     if (!(m_innerURLElement && m_innerURLElement->document()))
493         return false;
494
495     if (m_innerURLElement->hasTagName(aTag))
496         return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
497 #if ENABLE(SVG)
498     if (m_innerURLElement->hasTagName(SVGNames::aTag))
499         return m_innerURLElement->isLink();
500 #endif
501
502     return false;
503 }
504
505 String HitTestResult::titleDisplayString() const
506 {
507     if (!m_innerURLElement)
508         return String();
509     
510     return displayString(m_innerURLElement->title(), m_innerURLElement.get());
511 }
512
513 String HitTestResult::textContent() const
514 {
515     if (!m_innerURLElement)
516         return String();
517     return m_innerURLElement->textContent();
518 }
519
520 // FIXME: This function needs a better name and may belong in a different class. It's not
521 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
522 // function would make more sense in the ContextMenu class, except that WebElementDictionary 
523 // hooks into it. Anyway, we should architect this better. 
524 bool HitTestResult::isContentEditable() const
525 {
526     if (!m_innerNonSharedNode)
527         return false;
528
529     if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag))
530         return true;
531
532     if (m_innerNonSharedNode->hasTagName(inputTag))
533         return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
534
535     return m_innerNonSharedNode->rendererIsEditable();
536 }
537
538 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const LayoutPoint& pointInContainer, const IntRect& rect)
539 {
540     // If it is not a rect-based hit test, this method has to be no-op.
541     // Return false, so the hit test stops.
542     if (!isRectBasedTest())
543         return false;
544
545     // If node is null, return true so the hit test can continue.
546     if (!node)
547         return true;
548
549     node = node->shadowAncestorNode();
550     mutableRectBasedTestResult().add(node);
551
552     if (node->renderer()->isInline()) {
553         for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) {
554             if (!curr->isRenderInline())
555                 break;
556             
557             // We need to make sure the nodes for culled inlines get included.
558             RenderInline* currInline = toRenderInline(curr);
559             if (currInline->alwaysCreateLineBoxes())
560                 break;
561             
562             if (currInline->visibleToHitTesting() && currInline->node())
563                 mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode());
564         }
565     }
566     return !rect.contains(rectForPoint(pointInContainer));
567 }
568
569 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const LayoutPoint& pointInContainer, const FloatRect& rect)
570 {
571     // If it is not a rect-based hit test, this method has to be no-op.
572     // Return false, so the hit test stops.
573     if (!isRectBasedTest())
574         return false;
575
576     // If node is null, return true so the hit test can continue.
577     if (!node)
578         return true;
579
580     node = node->shadowAncestorNode();
581     mutableRectBasedTestResult().add(node);
582
583     if (node->renderer()->isInline()) {
584         for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) {
585             if (!curr->isRenderInline())
586                 break;
587             
588             // We need to make sure the nodes for culled inlines get included.
589             RenderInline* currInline = toRenderInline(curr);
590             if (currInline->alwaysCreateLineBoxes())
591                 break;
592             
593             if (currInline->visibleToHitTesting() && currInline->node())
594                 mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode());
595         }
596     }
597     return !rect.contains(rectForPoint(pointInContainer));
598 }
599
600 void HitTestResult::append(const HitTestResult& other)
601 {
602     ASSERT(isRectBasedTest() && other.isRectBasedTest());
603
604     if (!m_innerNode && other.innerNode()) {
605         m_innerNode = other.innerNode();
606         m_innerNonSharedNode = other.innerNonSharedNode();
607         m_localPoint = other.localPoint();
608         m_innerURLElement = other.URLElement();
609         m_scrollbar = other.scrollbar();
610         m_isOverWidget = other.isOverWidget();
611     }
612
613     if (other.m_rectBasedTestResult) {
614         NodeSet& set = mutableRectBasedTestResult();
615         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
616             set.add(it->get());
617     }
618 }
619
620 LayoutRect HitTestResult::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
621 {
622     LayoutPoint actualPoint(point);
623     actualPoint -= LayoutSize(leftPadding, topPadding);
624
625     IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding);
626     // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1".
627     actualPadding += IntSize(1, 1);
628
629     return LayoutRect(actualPoint, actualPadding);
630 }
631
632 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
633 {
634     if (!m_rectBasedTestResult)
635         m_rectBasedTestResult = adoptPtr(new NodeSet);
636     return *m_rectBasedTestResult;
637 }
638
639 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
640 {
641     if (!m_rectBasedTestResult)
642         m_rectBasedTestResult = adoptPtr(new NodeSet);
643     return *m_rectBasedTestResult;
644 }
645
646 } // namespace WebCore