1 /* 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21 #include "config.h" 22 #include "HitTestResult.h" 23 24 #include "Frame.h" 25 #include "FrameTree.h" 26 #include "HTMLAnchorElement.h" 27 #include "HTMLImageElement.h" 28 #include "HTMLInputElement.h" 29 #include "HTMLMediaElement.h" 30 #include "HTMLNames.h" 31 #include "RenderImage.h" 32 #include "Scrollbar.h" 33 #include "SelectionController.h" 34 35 #if ENABLE(SVG) 36 #include "SVGNames.h" 37 #include "XLinkNames.h" 38 #endif 39 40 #if ENABLE(WML) 41 #include "WMLImageElement.h" 42 #include "WMLNames.h" 43 #endif 44 45 namespace WebCore { 46 47 using namespace HTMLNames; 48 49 HitTestResult::HitTestResult(const IntPoint& point) 50 : m_point(point) 51 , m_isOverWidget(false) 52 { 53 } 54 55 HitTestResult::HitTestResult(const HitTestResult& other) 56 : m_innerNode(other.innerNode()) 57 , m_innerNonSharedNode(other.innerNonSharedNode()) 58 , m_point(other.point()) 59 , m_localPoint(other.localPoint()) 60 , m_innerURLElement(other.URLElement()) 61 , m_scrollbar(other.scrollbar()) 62 , m_isOverWidget(other.isOverWidget()) 63 { 64 } 65 66 HitTestResult::~HitTestResult() 67 { 68 } 69 70 HitTestResult& HitTestResult::operator=(const HitTestResult& other) 71 { 72 m_innerNode = other.innerNode(); 73 m_innerNonSharedNode = other.innerNonSharedNode(); 74 m_point = other.point(); 75 m_localPoint = other.localPoint(); 76 m_innerURLElement = other.URLElement(); 77 m_scrollbar = other.scrollbar(); 78 m_isOverWidget = other.isOverWidget(); 79 return *this; 80 } 81 82 void HitTestResult::setToNonShadowAncestor() 83 { 84 Node* node = innerNode(); 85 if (node) 86 node = node->shadowAncestorNode(); 87 setInnerNode(node); 88 node = innerNonSharedNode(); 89 if (node) 90 node = node->shadowAncestorNode(); 91 setInnerNonSharedNode(node); 92 } 93 94 void HitTestResult::setInnerNode(Node* n) 95 { 96 m_innerNode = n; 97 } 98 99 void HitTestResult::setInnerNonSharedNode(Node* n) 100 { 101 m_innerNonSharedNode = n; 102 } 103 104 void HitTestResult::setURLElement(Element* n) 105 { 106 m_innerURLElement = n; 107 } 108 109 void HitTestResult::setScrollbar(Scrollbar* s) 110 { 111 m_scrollbar = s; 112 } 113 114 Frame* HitTestResult::targetFrame() const 115 { 116 if (!m_innerURLElement) 117 return 0; 118 119 Frame* frame = m_innerURLElement->document()->frame(); 120 if (!frame) 121 return 0; 122 123 return frame->tree()->find(m_innerURLElement->target()); 124 } 125 126 bool HitTestResult::isSelected() const 127 { 128 if (!m_innerNonSharedNode) 129 return false; 130 131 Frame* frame = m_innerNonSharedNode->document()->frame(); 132 if (!frame) 133 return false; 134 135 return frame->selection()->contains(m_point); 136 } 137 138 String HitTestResult::spellingToolTip(TextDirection& dir) const 139 { 140 dir = LTR; 141 // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar 142 // currently supply strings, but maybe someday markers associated with misspelled words will also. 143 if (!m_innerNonSharedNode) 144 return String(); 145 146 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Grammar); 147 if (!marker) 148 return String(); 149 150 if (RenderObject* renderer = m_innerNonSharedNode->renderer()) 151 dir = renderer->style()->direction(); 152 return marker->description; 153 } 154 155 String HitTestResult::replacedString() const 156 { 157 // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected, 158 // and is used for generating a contextual menu item that allows it to easily be changed back if desired. 159 if (!m_innerNonSharedNode) 160 return String(); 161 162 DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Replacement); 163 if (!marker) 164 return String(); 165 166 return marker->description; 167 } 168 169 String HitTestResult::title(TextDirection& dir) const 170 { 171 dir = LTR; 172 // Find the title in the nearest enclosing DOM node. 173 // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it. 174 for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) { 175 if (titleNode->isElementNode()) { 176 String title = static_cast<Element*>(titleNode)->title(); 177 if (!title.isEmpty()) { 178 if (RenderObject* renderer = titleNode->renderer()) 179 dir = renderer->style()->direction(); 180 return title; 181 } 182 } 183 } 184 return String(); 185 } 186 187 String displayString(const String& string, const Node* node) 188 { 189 if (!node) 190 return string; 191 return node->document()->displayStringModifiedByEncoding(string); 192 } 193 194 String HitTestResult::altDisplayString() const 195 { 196 if (!m_innerNonSharedNode) 197 return String(); 198 199 if (m_innerNonSharedNode->hasTagName(imgTag)) { 200 HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get()); 201 return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get()); 202 } 203 204 if (m_innerNonSharedNode->hasTagName(inputTag)) { 205 HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get()); 206 return displayString(input->alt(), m_innerNonSharedNode.get()); 207 } 208 209 #if ENABLE(WML) 210 if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) { 211 WMLImageElement* image = static_cast<WMLImageElement*>(m_innerNonSharedNode.get()); 212 return displayString(image->altText(), m_innerNonSharedNode.get()); 213 } 214 #endif 215 216 return String(); 217 } 218 219 Image* HitTestResult::image() const 220 { 221 if (!m_innerNonSharedNode) 222 return 0; 223 224 RenderObject* renderer = m_innerNonSharedNode->renderer(); 225 if (renderer && renderer->isImage()) { 226 RenderImage* image = static_cast<WebCore::RenderImage*>(renderer); 227 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 228 return image->cachedImage()->image(); 229 } 230 231 return 0; 232 } 233 234 IntRect HitTestResult::imageRect() const 235 { 236 if (!image()) 237 return IntRect(); 238 return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox(); 239 } 240 241 KURL HitTestResult::absoluteImageURL() const 242 { 243 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document())) 244 return KURL(); 245 246 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage())) 247 return KURL(); 248 249 AtomicString urlString; 250 if (m_innerNonSharedNode->hasTagName(embedTag) 251 || m_innerNonSharedNode->hasTagName(imgTag) 252 || m_innerNonSharedNode->hasTagName(inputTag) 253 || m_innerNonSharedNode->hasTagName(objectTag) 254 #if ENABLE(SVG) 255 || m_innerNonSharedNode->hasTagName(SVGNames::imageTag) 256 #endif 257 #if ENABLE(WML) 258 || m_innerNonSharedNode->hasTagName(WMLNames::imgTag) 259 #endif 260 ) { 261 Element* element = static_cast<Element*>(m_innerNonSharedNode.get()); 262 urlString = element->getAttribute(element->imageSourceAttributeName()); 263 } else 264 return KURL(); 265 266 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString)); 267 } 268 269 KURL HitTestResult::absoluteMediaURL() const 270 { 271 #if ENABLE(VIDEO) 272 if (!(m_innerNonSharedNode && m_innerNonSharedNode->document())) 273 return KURL(); 274 275 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia())) 276 return KURL(); 277 278 AtomicString urlString; 279 if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag)) { 280 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get()); 281 urlString = mediaElement->currentSrc(); 282 } else 283 return KURL(); 284 285 return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString)); 286 #else 287 return KURL(); 288 #endif 289 } 290 291 KURL HitTestResult::absoluteLinkURL() const 292 { 293 if (!(m_innerURLElement && m_innerURLElement->document())) 294 return KURL(); 295 296 AtomicString urlString; 297 if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag)) 298 urlString = m_innerURLElement->getAttribute(hrefAttr); 299 #if ENABLE(SVG) 300 else if (m_innerURLElement->hasTagName(SVGNames::aTag)) 301 urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr); 302 #endif 303 #if ENABLE(WML) 304 else if (m_innerURLElement->hasTagName(WMLNames::aTag)) 305 urlString = m_innerURLElement->getAttribute(hrefAttr); 306 #endif 307 else 308 return KURL(); 309 310 return m_innerURLElement->document()->completeURL(deprecatedParseURL(urlString)); 311 } 312 313 bool HitTestResult::isLiveLink() const 314 { 315 if (!(m_innerURLElement && m_innerURLElement->document())) 316 return false; 317 318 if (m_innerURLElement->hasTagName(aTag)) 319 return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink(); 320 #if ENABLE(SVG) 321 if (m_innerURLElement->hasTagName(SVGNames::aTag)) 322 return m_innerURLElement->isLink(); 323 #endif 324 #if ENABLE(WML) 325 if (m_innerURLElement->hasTagName(WMLNames::aTag)) 326 return m_innerURLElement->isLink(); 327 #endif 328 329 return false; 330 } 331 332 String HitTestResult::titleDisplayString() const 333 { 334 if (!m_innerURLElement) 335 return String(); 336 337 return displayString(m_innerURLElement->title(), m_innerURLElement.get()); 338 } 339 340 String HitTestResult::textContent() const 341 { 342 if (!m_innerURLElement) 343 return String(); 344 return m_innerURLElement->textContent(); 345 } 346 347 // FIXME: This function needs a better name and may belong in a different class. It's not 348 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this 349 // function would make more sense in the ContextMenu class, except that WebElementDictionary 350 // hooks into it. Anyway, we should architect this better. 351 bool HitTestResult::isContentEditable() const 352 { 353 if (!m_innerNonSharedNode) 354 return false; 355 356 if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag)) 357 return true; 358 359 if (m_innerNonSharedNode->hasTagName(inputTag)) 360 return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField(); 361 362 return m_innerNonSharedNode->isContentEditable(); 363 } 364 365 } // namespace WebCore 366