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