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