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