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