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