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