Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "AXObjectCache.h"
     31 
     32 #include "AccessibilityARIAGrid.h"
     33 #include "AccessibilityARIAGridCell.h"
     34 #include "AccessibilityARIAGridRow.h"
     35 #include "AccessibilityImageMapLink.h"
     36 #include "AccessibilityList.h"
     37 #include "AccessibilityListBox.h"
     38 #include "AccessibilityListBoxOption.h"
     39 #include "AccessibilityMediaControls.h"
     40 #include "AccessibilityMenuList.h"
     41 #include "AccessibilityMenuListOption.h"
     42 #include "AccessibilityMenuListPopup.h"
     43 #include "AccessibilityProgressIndicator.h"
     44 #include "AccessibilityRenderObject.h"
     45 #include "AccessibilityScrollView.h"
     46 #include "AccessibilityScrollbar.h"
     47 #include "AccessibilitySlider.h"
     48 #include "AccessibilityTable.h"
     49 #include "AccessibilityTableCell.h"
     50 #include "AccessibilityTableColumn.h"
     51 #include "AccessibilityTableHeaderContainer.h"
     52 #include "AccessibilityTableRow.h"
     53 #include "Document.h"
     54 #include "FocusController.h"
     55 #include "Frame.h"
     56 #include "HTMLAreaElement.h"
     57 #include "HTMLImageElement.h"
     58 #include "HTMLNames.h"
     59 #if ENABLE(VIDEO)
     60 #include "MediaControlElements.h"
     61 #endif
     62 #include "InputElement.h"
     63 #include "Page.h"
     64 #include "RenderListBox.h"
     65 #include "RenderMenuList.h"
     66 #include "RenderProgress.h"
     67 #include "RenderSlider.h"
     68 #include "RenderTable.h"
     69 #include "RenderTableCell.h"
     70 #include "RenderTableRow.h"
     71 #include "RenderView.h"
     72 #include "ScrollView.h"
     73 
     74 #include <wtf/PassRefPtr.h>
     75 
     76 namespace WebCore {
     77 
     78 using namespace HTMLNames;
     79 
     80 bool AXObjectCache::gAccessibilityEnabled = false;
     81 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
     82 
     83 AXObjectCache::AXObjectCache(const Document* doc)
     84     : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
     85 {
     86     m_document = const_cast<Document*>(doc);
     87 }
     88 
     89 AXObjectCache::~AXObjectCache()
     90 {
     91     HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
     92     for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
     93         AccessibilityObject* obj = (*it).second.get();
     94         detachWrapper(obj);
     95         obj->detach();
     96         removeAXID(obj);
     97     }
     98 }
     99 
    100 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
    101 {
    102     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
    103     // in the list of children for its corresponding image.
    104     if (!areaElement)
    105         return 0;
    106 
    107     HTMLImageElement* imageElement = areaElement->imageElement();
    108     if (!imageElement)
    109         return 0;
    110 
    111     AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
    112     if (!axRenderImage)
    113         return 0;
    114 
    115     AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
    116     unsigned count = imageChildren.size();
    117     for (unsigned k = 0; k < count; ++k) {
    118         AccessibilityObject* child = imageChildren[k].get();
    119         if (!child->isImageMapLink())
    120             continue;
    121 
    122         if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
    123             return child;
    124     }
    125 
    126     return 0;
    127 }
    128 
    129 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
    130 {
    131     // get the focused node in the page
    132     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
    133     Node* focusedNode = focusedDocument->focusedNode();
    134     if (!focusedNode)
    135         focusedNode = focusedDocument;
    136 
    137     if (focusedNode->hasTagName(areaTag))
    138         return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
    139 
    140     RenderObject* focusedNodeRenderer = focusedNode->renderer();
    141     if (!focusedNodeRenderer)
    142         return 0;
    143 
    144     AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
    145 
    146     if (obj->shouldFocusActiveDescendant()) {
    147         if (AccessibilityObject* descendant = obj->activeDescendant())
    148             obj = descendant;
    149     }
    150 
    151     // the HTML element, for example, is focusable but has an AX object that is ignored
    152     if (obj->accessibilityIsIgnored())
    153         obj = obj->parentObjectUnignored();
    154 
    155     return obj;
    156 }
    157 
    158 AccessibilityObject* AXObjectCache::get(Widget* widget)
    159 {
    160     if (!widget)
    161         return 0;
    162 
    163     AXID axID = m_widgetObjectMapping.get(widget);
    164     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
    165     if (!axID)
    166         return 0;
    167 
    168     return m_objects.get(axID).get();
    169 }
    170 
    171 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
    172 {
    173     if (!renderer)
    174         return 0;
    175 
    176     AXID axID = m_renderObjectMapping.get(renderer);
    177     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
    178     if (!axID)
    179         return 0;
    180 
    181     return m_objects.get(axID).get();
    182 }
    183 
    184 // FIXME: This probably belongs on Node.
    185 // FIXME: This should take a const char*, but one caller passes nullAtom.
    186 bool nodeHasRole(Node* node, const String& role)
    187 {
    188     if (!node || !node->isElementNode())
    189         return false;
    190 
    191     return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
    192 }
    193 
    194 static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
    195 {
    196     // FIXME: How could renderer->node() ever not be an Element?
    197     Node* node = renderer->node();
    198 
    199     // If the node is aria role="list" or the aria role is empty and its a
    200     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
    201     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
    202                       || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
    203         return AccessibilityList::create(renderer);
    204 
    205     // aria tables
    206     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
    207         return AccessibilityARIAGrid::create(renderer);
    208     if (nodeHasRole(node, "row"))
    209         return AccessibilityARIAGridRow::create(renderer);
    210     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
    211         return AccessibilityARIAGridCell::create(renderer);
    212 
    213 #if ENABLE(VIDEO)
    214     // media controls
    215     if (node && node->isMediaControlElement())
    216         return AccessibilityMediaControl::create(renderer);
    217 #endif
    218 
    219     if (renderer->isBoxModelObject()) {
    220         RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
    221         if (cssBox->isListBox())
    222             return AccessibilityListBox::create(toRenderListBox(cssBox));
    223         if (cssBox->isMenuList())
    224             return AccessibilityMenuList::create(toRenderMenuList(cssBox));
    225 
    226         // standard tables
    227         if (cssBox->isTable())
    228             return AccessibilityTable::create(toRenderTable(cssBox));
    229         if (cssBox->isTableRow())
    230             return AccessibilityTableRow::create(toRenderTableRow(cssBox));
    231         if (cssBox->isTableCell())
    232             return AccessibilityTableCell::create(toRenderTableCell(cssBox));
    233 
    234 #if ENABLE(PROGRESS_TAG)
    235         // progress bar
    236         if (cssBox->isProgress())
    237             return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
    238 #endif
    239 
    240         // input type=range
    241         if (cssBox->isSlider())
    242             return AccessibilitySlider::create(toRenderSlider(cssBox));
    243     }
    244 
    245     return AccessibilityRenderObject::create(renderer);
    246 }
    247 
    248 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
    249 {
    250     if (!widget)
    251         return 0;
    252 
    253     if (AccessibilityObject* obj = get(widget))
    254         return obj;
    255 
    256     RefPtr<AccessibilityObject> newObj = 0;
    257     if (widget->isFrameView())
    258         newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
    259     else if (widget->isScrollbar())
    260         newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
    261 
    262     getAXID(newObj.get());
    263 
    264     m_widgetObjectMapping.set(widget, newObj->axObjectID());
    265     m_objects.set(newObj->axObjectID(), newObj);
    266     attachWrapper(newObj.get());
    267     return newObj.get();
    268 }
    269 
    270 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
    271 {
    272     if (!renderer)
    273         return 0;
    274 
    275     if (AccessibilityObject* obj = get(renderer))
    276         return obj;
    277 
    278     RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
    279 
    280     getAXID(newObj.get());
    281 
    282     m_renderObjectMapping.set(renderer, newObj->axObjectID());
    283     m_objects.set(newObj->axObjectID(), newObj);
    284     attachWrapper(newObj.get());
    285     return newObj.get();
    286 }
    287 
    288 AccessibilityObject* AXObjectCache::rootObject()
    289 {
    290     return getOrCreate(m_document->view());
    291 }
    292 
    293 AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
    294 {
    295     if (!frame)
    296         return 0;
    297     return getOrCreate(frame->view());
    298 }
    299 
    300 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
    301 {
    302     RefPtr<AccessibilityObject> obj = 0;
    303 
    304     // will be filled in...
    305     switch (role) {
    306     case ListBoxOptionRole:
    307         obj = AccessibilityListBoxOption::create();
    308         break;
    309     case ImageMapLinkRole:
    310         obj = AccessibilityImageMapLink::create();
    311         break;
    312     case ColumnRole:
    313         obj = AccessibilityTableColumn::create();
    314         break;
    315     case TableHeaderContainerRole:
    316         obj = AccessibilityTableHeaderContainer::create();
    317         break;
    318     case SliderThumbRole:
    319         obj = AccessibilitySliderThumb::create();
    320         break;
    321     case MenuListPopupRole:
    322         obj = AccessibilityMenuListPopup::create();
    323         break;
    324     case MenuListOptionRole:
    325         obj = AccessibilityMenuListOption::create();
    326         break;
    327     default:
    328         obj = 0;
    329     }
    330 
    331     if (obj)
    332         getAXID(obj.get());
    333     else
    334         return 0;
    335 
    336     m_objects.set(obj->axObjectID(), obj);
    337     attachWrapper(obj.get());
    338     return obj.get();
    339 }
    340 
    341 void AXObjectCache::remove(AXID axID)
    342 {
    343     if (!axID)
    344         return;
    345 
    346     // first fetch object to operate some cleanup functions on it
    347     AccessibilityObject* obj = m_objects.get(axID).get();
    348     if (!obj)
    349         return;
    350 
    351     detachWrapper(obj);
    352     obj->detach();
    353     removeAXID(obj);
    354 
    355     // finally remove the object
    356     if (!m_objects.take(axID))
    357         return;
    358 
    359     ASSERT(m_objects.size() >= m_idsInUse.size());
    360 }
    361 
    362 void AXObjectCache::remove(RenderObject* renderer)
    363 {
    364     if (!renderer)
    365         return;
    366 
    367     AXID axID = m_renderObjectMapping.get(renderer);
    368     remove(axID);
    369     m_renderObjectMapping.remove(renderer);
    370 }
    371 
    372 void AXObjectCache::remove(Widget* view)
    373 {
    374     if (!view)
    375         return;
    376 
    377     AXID axID = m_widgetObjectMapping.get(view);
    378     remove(axID);
    379     m_widgetObjectMapping.remove(view);
    380 }
    381 
    382 
    383 #if !PLATFORM(WIN) || OS(WINCE)
    384 AXID AXObjectCache::platformGenerateAXID() const
    385 {
    386     static AXID lastUsedID = 0;
    387 
    388     // Generate a new ID.
    389     AXID objID = lastUsedID;
    390     do {
    391         ++objID;
    392     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
    393 
    394     lastUsedID = objID;
    395 
    396     return objID;
    397 }
    398 #endif
    399 
    400 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
    401 {
    402     // check for already-assigned ID
    403     AXID objID = obj->axObjectID();
    404     if (objID) {
    405         ASSERT(m_idsInUse.contains(objID));
    406         return objID;
    407     }
    408 
    409     objID = platformGenerateAXID();
    410 
    411     m_idsInUse.add(objID);
    412     obj->setAXObjectID(objID);
    413 
    414     return objID;
    415 }
    416 
    417 void AXObjectCache::removeAXID(AccessibilityObject* object)
    418 {
    419     if (!object)
    420         return;
    421 
    422     AXID objID = object->axObjectID();
    423     if (!objID)
    424         return;
    425     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
    426     ASSERT(m_idsInUse.contains(objID));
    427     object->setAXObjectID(0);
    428     m_idsInUse.remove(objID);
    429 }
    430 
    431 #if HAVE(ACCESSIBILITY)
    432 void AXObjectCache::contentChanged(RenderObject* renderer)
    433 {
    434     AccessibilityObject* object = getOrCreate(renderer);
    435     if (object)
    436         object->contentChanged();
    437 }
    438 #endif
    439 
    440 void AXObjectCache::childrenChanged(RenderObject* renderer)
    441 {
    442     if (!renderer)
    443         return;
    444 
    445     AXID axID = m_renderObjectMapping.get(renderer);
    446     if (!axID)
    447         return;
    448 
    449     AccessibilityObject* obj = m_objects.get(axID).get();
    450     if (obj)
    451         obj->childrenChanged();
    452 }
    453 
    454 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
    455 {
    456     m_notificationPostTimer.stop();
    457 
    458     unsigned i = 0, count = m_notificationsToPost.size();
    459     for (i = 0; i < count; ++i) {
    460         AccessibilityObject* obj = m_notificationsToPost[i].first.get();
    461 #ifndef NDEBUG
    462         // Make sure none of the render views are in the process of being layed out.
    463         // Notifications should only be sent after the renderer has finished
    464         if (obj->isAccessibilityRenderObject()) {
    465             AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
    466             RenderObject* renderer = renderObj->renderer();
    467             if (renderer && renderer->view())
    468                 ASSERT(!renderer->view()->layoutState());
    469         }
    470 #endif
    471 
    472         postPlatformNotification(obj, m_notificationsToPost[i].second);
    473     }
    474 
    475     m_notificationsToPost.clear();
    476 }
    477 
    478 #if HAVE(ACCESSIBILITY)
    479 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
    480 {
    481     // Notifications for text input objects are sent to that object.
    482     // All others are sent to the top WebArea.
    483     if (!renderer)
    484         return;
    485 
    486     // Get an accessibility object that already exists. One should not be created here
    487     // because a render update may be in progress and creating an AX object can re-trigger a layout
    488     RefPtr<AccessibilityObject> object = get(renderer);
    489     while (!object && renderer) {
    490         renderer = renderer->parent();
    491         object = get(renderer);
    492     }
    493 
    494     if (!renderer)
    495         return;
    496 
    497     postNotification(object.get(), renderer->document(), notification, postToElement, postType);
    498 }
    499 
    500 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
    501 {
    502     if (object && !postToElement)
    503         object = object->observableObject();
    504 
    505     if (!object && document)
    506         object = get(document->renderer());
    507 
    508     if (!object)
    509         return;
    510 
    511     if (postType == PostAsynchronously) {
    512         m_notificationsToPost.append(make_pair(object, notification));
    513         if (!m_notificationPostTimer.isActive())
    514             m_notificationPostTimer.startOneShot(0);
    515     } else
    516         postPlatformNotification(object, notification);
    517 }
    518 
    519 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
    520 {
    521     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
    522     // to find the container which should send out the notification.
    523     postNotification(renderer, AXSelectedChildrenChanged, false);
    524 }
    525 
    526 void AXObjectCache::nodeTextChangeNotification(RenderObject* renderer, AXTextChange textChange, unsigned offset, unsigned count)
    527 {
    528     if (!renderer)
    529         return;
    530 
    531     // Delegate on the right platform
    532     AccessibilityObject* obj = getOrCreate(renderer);
    533     nodeTextChangePlatformNotification(obj, textChange, offset, count);
    534 }
    535 #endif
    536 
    537 #if HAVE(ACCESSIBILITY)
    538 
    539 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
    540 {
    541     if (!view)
    542         return;
    543 
    544     // We don't want to create a scroll view from this method, only update an existing one.
    545     AccessibilityObject* scrollViewObject = get(view);
    546     if (scrollViewObject)
    547         scrollViewObject->updateChildrenIfNecessary();
    548 }
    549 
    550 void AXObjectCache::handleAriaExpandedChange(RenderObject *renderer)
    551 {
    552     if (!renderer)
    553         return;
    554     AccessibilityObject* obj = getOrCreate(renderer);
    555     if (obj)
    556         obj->handleAriaExpandedChanged();
    557 }
    558 
    559 void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
    560 {
    561     if (!renderer)
    562         return;
    563     AccessibilityObject* obj = getOrCreate(renderer);
    564     if (obj)
    565         obj->handleActiveDescendantChanged();
    566 }
    567 
    568 void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
    569 {
    570     if (!renderer)
    571         return;
    572     AccessibilityObject* obj = getOrCreate(renderer);
    573     if (obj && obj->isAccessibilityRenderObject())
    574         static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
    575 }
    576 #endif
    577 
    578 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
    579 {
    580     if (!isNodeInUse(textMarkerData.node))
    581         return VisiblePosition();
    582 
    583     // FIXME: Accessability should make it clear these are DOM-compliant offsets or store Position objects.
    584     VisiblePosition visiblePos = VisiblePosition(Position(textMarkerData.node, textMarkerData.offset), textMarkerData.affinity);
    585     Position deepPos = visiblePos.deepEquivalent();
    586     if (deepPos.isNull())
    587         return VisiblePosition();
    588 
    589     RenderObject* renderer = deepPos.deprecatedNode()->renderer();
    590     if (!renderer)
    591         return VisiblePosition();
    592 
    593     AXObjectCache* cache = renderer->document()->axObjectCache();
    594     if (!cache->isIDinUse(textMarkerData.axID))
    595         return VisiblePosition();
    596 
    597     if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
    598         return VisiblePosition();
    599 
    600     return visiblePos;
    601 }
    602 
    603 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
    604 {
    605     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
    606     // This also allows callers to check for failure by looking at textMarkerData upon return.
    607     memset(&textMarkerData, 0, sizeof(TextMarkerData));
    608 
    609     if (visiblePos.isNull())
    610         return;
    611 
    612     Position deepPos = visiblePos.deepEquivalent();
    613     Node* domNode = deepPos.deprecatedNode();
    614     ASSERT(domNode);
    615     if (!domNode)
    616         return;
    617 
    618     if (domNode->isHTMLElement()) {
    619         InputElement* inputElement = domNode->toInputElement();
    620         if (inputElement && inputElement->isPasswordField())
    621             return;
    622     }
    623 
    624     // locate the renderer, which must exist for a visible dom node
    625     RenderObject* renderer = domNode->renderer();
    626     ASSERT(renderer);
    627 
    628     // find or create an accessibility object for this renderer
    629     AXObjectCache* cache = renderer->document()->axObjectCache();
    630     RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
    631 
    632     textMarkerData.axID = obj.get()->axObjectID();
    633     textMarkerData.node = domNode;
    634     textMarkerData.offset = deepPos.deprecatedEditingOffset();
    635     textMarkerData.affinity = visiblePos.affinity();
    636 
    637     cache->setNodeInUse(domNode);
    638 }
    639 
    640 } // namespace WebCore
    641