text: /*
   * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
   */
  
  #include "config.h"
  #include "WebKitDLL.h"
  #include "AccessibleBase.h"
  
  #include "AccessibleImage.h"
  #include "WebView.h"
  #include <WebCore/AccessibilityListBox.h>
  #include <WebCore/AccessibilityMenuListPopup.h>
  #include <WebCore/AccessibilityObject.h>
  #include <WebCore/AXObjectCache.h>
  #include <WebCore/BString.h>
  #include <WebCore/Element.h>
  #include <WebCore/EventHandler.h>
  #include <WebCore/FrameView.h>
  #include <WebCore/HostWindow.h>
  #include <WebCore/HTMLNames.h>
  #include <WebCore/HTMLFrameElementBase.h>
  #include <WebCore/HTMLInputElement.h>
  #include <WebCore/IntRect.h>
  #include <WebCore/PlatformEvent.h>
  #include <WebCore/RenderFrame.h>
  #include <WebCore/RenderObject.h>
  #include <WebCore/RenderView.h>
  #include <oleacc.h>
  #include <wtf/RefPtr.h>
  
  using namespace WebCore;
  
  AccessibleBase::AccessibleBase(AccessibilityObject* obj)
      : AccessibilityObjectWrapper(obj)
      , m_refCount(0)
  {
      ASSERT_ARG(obj, obj);
      m_object->setWrapper(this);
      ++gClassCount;
      gClassNameCount.add("AccessibleBase");
  }
  
  AccessibleBase::~AccessibleBase()
  {
      --gClassCount;
      gClassNameCount.remove("AccessibleBase");
  }
  
  AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
  {
      ASSERT_ARG(obj, obj);
  
      if (obj->isImage())
          return new AccessibleImage(obj);
  
      return new AccessibleBase(obj);
  }
  
  HRESULT AccessibleBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
  {
      if (!IsEqualGUID(guidService, SID_AccessibleComparable)) {
          *ppvObject = 0;
          return E_INVALIDARG;
      }
      return QueryInterface(riid, ppvObject);
  }
  
  // IUnknown
  HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
  {
      if (IsEqualGUID(riid, __uuidof(IAccessible)))
          *ppvObject = static_cast<IAccessible*>(this);
      else if (IsEqualGUID(riid, __uuidof(IDispatch)))
          *ppvObject = static_cast<IAccessible*>(this);
      else if (IsEqualGUID(riid, __uuidof(IUnknown)))
          *ppvObject = static_cast<IAccessible*>(this);
      else if (IsEqualGUID(riid, __uuidof(IAccessibleComparable)))
          *ppvObject = static_cast<IAccessibleComparable*>(this);
      else if (IsEqualGUID(riid, __uuidof(IServiceProvider)))
          *ppvObject = static_cast<IServiceProvider*>(this);
      else if (IsEqualGUID(riid, __uuidof(AccessibleBase)))
          *ppvObject = static_cast<AccessibleBase*>(this);
      else {
          *ppvObject = 0;
          return E_NOINTERFACE;
      }
      AddRef();
      return S_OK;
  }
  
  ULONG STDMETHODCALLTYPE AccessibleBase::Release(void)
  {
      ASSERT(m_refCount > 0);
      if (--m_refCount)
          return m_refCount;
      delete this;
      return 0;
  }
  
  // IAccessible
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accParent(IDispatch** parent)
  {
      *parent = 0;
  
      if (!m_object)
          return E_FAIL;
  
      AccessibilityObject* parentObject = m_object->parentObjectUnignored();
      if (parentObject) {
          *parent = wrapper(parentObject);
          (*parent)->AddRef();
          return S_OK;
      }
  
      if (!m_object->topDocumentFrameView())
          return E_FAIL;
  
      return WebView::AccessibleObjectFromWindow(m_object->topDocumentFrameView()->hostWindow()->platformPageClient(),
          OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent));
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count)
  {
      if (!m_object)
          return E_FAIL;
      if (!count)
          return E_POINTER;
      *count = static_cast<long>(m_object->children().size());
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild)
  {
      if (!ppChild)
          return E_POINTER;
  
      *ppChild = 0;
  
      AccessibilityObject* childObj;
  
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
      if (FAILED(hr))
          return hr;
  
      *ppChild = static_cast<IDispatch*>(wrapper(childObj));
      (*ppChild)->AddRef();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name)
  {
      if (!name)
          return E_POINTER;
  
      *name = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (*name = BString(wrapper(childObj)->name()).release())
          return S_OK;
      return S_FALSE;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value)
  {
      if (!value)
          return E_POINTER;
  
      *value = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (*value = BString(wrapper(childObj)->value()).release())
          return S_OK;
      return S_FALSE;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description)
  {
      if (!description)
          return E_POINTER;
  
      *description = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (*description = BString(childObj->descriptionForMSAA()).release())
          return S_OK;
  
      return S_FALSE;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole)
  {
      if (!pvRole)
          return E_POINTER;
  
      ::VariantInit(pvRole);
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      String roleString = childObj->stringRoleForMSAA();
      if (!roleString.isEmpty()) {
          V_VT(pvRole) = VT_BSTR;
          V_BSTR(pvRole) = BString(roleString).release();
          return S_OK;
      }
  
      pvRole->vt = VT_I4;
      pvRole->lVal = wrapper(childObj)->role();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState)
  {
      if (!pvState)
          return E_POINTER;
  
      ::VariantInit(pvState);
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      pvState->vt = VT_I4;
      pvState->lVal = 0;
  
      if (childObj->isLinked())
          pvState->lVal |= STATE_SYSTEM_LINKED;
  
      if (childObj->isHovered())
          pvState->lVal |= STATE_SYSTEM_HOTTRACKED;
  
      if (!childObj->isEnabled())
          pvState->lVal |= STATE_SYSTEM_UNAVAILABLE;
  
      if (childObj->isReadOnly())
          pvState->lVal |= STATE_SYSTEM_READONLY;
  
      if (childObj->isOffScreen())
          pvState->lVal |= STATE_SYSTEM_OFFSCREEN;
  
      if (childObj->isPasswordField())
          pvState->lVal |= STATE_SYSTEM_PROTECTED;
  
      if (childObj->isIndeterminate())
          pvState->lVal |= STATE_SYSTEM_INDETERMINATE;
  
      if (childObj->isChecked())
          pvState->lVal |= STATE_SYSTEM_CHECKED;
  
      if (childObj->isPressed())
          pvState->lVal |= STATE_SYSTEM_PRESSED;
  
      if (childObj->isFocused())
          pvState->lVal |= STATE_SYSTEM_FOCUSED;
  
      if (childObj->isVisited())
          pvState->lVal |= STATE_SYSTEM_TRAVERSED;
  
      if (childObj->canSetFocusAttribute())
          pvState->lVal |= STATE_SYSTEM_FOCUSABLE;
  
      if (childObj->isSelected())
          pvState->lVal |= STATE_SYSTEM_SELECTED;
  
      if (childObj->canSetSelectedAttribute())
          pvState->lVal |= STATE_SYSTEM_SELECTABLE;
  
      if (childObj->isMultiSelectable())
          pvState->lVal |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
  
      if (!childObj->isVisible())
          pvState->lVal |= STATE_SYSTEM_INVISIBLE;
  
      if (childObj->isCollapsed())
          pvState->lVal |= STATE_SYSTEM_COLLAPSED;
  
      if (childObj->roleValue() == PopUpButtonRole) {
          pvState->lVal |= STATE_SYSTEM_HASPOPUP;
  
          if (!childObj->isCollapsed())
              pvState->lVal |= STATE_SYSTEM_EXPANDED;
      }
  
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText)
  {
      if (!helpText)
          return E_POINTER;
  
      *helpText = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (*helpText = BString(childObj->helpText()).release())
          return S_OK;
      return S_FALSE;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut)
  {
      if (!shortcut)
          return E_POINTER;
  
      *shortcut = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      String accessKey = childObj->accessKey();
      if (accessKey.isNull())
          return S_FALSE;
  
      static String accessKeyModifiers;
      if (accessKeyModifiers.isNull()) {
          unsigned modifiers = EventHandler::accessKeyModifiers();
          // Follow the same order as Mozilla MSAA implementation:
          // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings
          // should not be localized and defines the separator as "+".
          if (modifiers & PlatformEvent::CtrlKey)
              accessKeyModifiers += "Ctrl+";
          if (modifiers & PlatformEvent::AltKey)
              accessKeyModifiers += "Alt+";
          if (modifiers & PlatformEvent::ShiftKey)
              accessKeyModifiers += "Shift+";
          if (modifiers & PlatformEvent::MetaKey)
              accessKeyModifiers += "Win+";
      }
      *shortcut = BString(String(accessKeyModifiers + accessKey)).release();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long selectionFlags, VARIANT vChild)
  {
      // According to MSDN, these combinations are invalid.
      if (((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION))
          || ((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION))
          || ((selectionFlags & (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION))
          || ((selectionFlags & (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION)))
          return E_INVALIDARG;
  
      AccessibilityObject* childObject;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObject);
  
      if (FAILED(hr))
          return hr;
  
      if (selectionFlags & SELFLAG_TAKEFOCUS)
          childObject->setFocused(true);
  
      AccessibilityObject* parentObject = childObject->parentObject();
      if (!parentObject)
          return E_INVALIDARG;
  
      if (selectionFlags & SELFLAG_TAKESELECTION) {
          if (parentObject->isListBox()) {
              Vector<RefPtr<AccessibilityObject> > selectedChildren(1);
              selectedChildren[0] = childObject;
              static_cast<AccessibilityListBox*>(parentObject)->setSelectedChildren(selectedChildren);
          } else { // any element may be selectable by virtue of it having the aria-selected property
              ASSERT(!parentObject->isMultiSelectable());
              childObject->setSelected(true);
          }
      }
  
      // MSDN says that ADD, REMOVE, and EXTENDSELECTION with no other flags are invalid for
      // single-select.
      const long allSELFLAGs = SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION | SELFLAG_EXTENDSELECTION | SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION;
      if (!parentObject->isMultiSelectable()
          && (((selectionFlags & allSELFLAGs) == SELFLAG_ADDSELECTION)
          || ((selectionFlags & allSELFLAGs) == SELFLAG_REMOVESELECTION)
          || ((selectionFlags & allSELFLAGs) == SELFLAG_EXTENDSELECTION)))
          return E_INVALIDARG;
  
      if (selectionFlags & SELFLAG_ADDSELECTION)
          childObject->setSelected(true);
  
      if (selectionFlags & SELFLAG_REMOVESELECTION)
          childObject->setSelected(false);
  
      // FIXME: Should implement SELFLAG_EXTENDSELECTION. For now, we just return
      // S_OK, matching Firefox.
  
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*)
  {
      return E_NOTIMPL;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild)
  {
      if (!pvFocusedChild)
          return E_POINTER;
  
      ::VariantInit(pvFocusedChild);
  
      if (!m_object)
          return E_FAIL;
  
      AccessibilityObject* focusedObj = m_object->focusedUIElement();
      if (!focusedObj)
          return S_FALSE;
  
      if (focusedObj == m_object) {
          V_VT(pvFocusedChild) = VT_I4;
          V_I4(pvFocusedChild) = CHILDID_SELF;
          return S_OK;
      }
  
      V_VT(pvFocusedChild) = VT_DISPATCH;
      V_DISPATCH(pvFocusedChild) = wrapper(focusedObj);
      V_DISPATCH(pvFocusedChild)->AddRef();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action)
  {
      if (!action)
          return E_POINTER;
  
      *action = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (*action = BString(childObj->actionVerb()).release())
          return S_OK;
      return S_FALSE;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild)
  {
      if (!left || !top || !width || !height)
          return E_POINTER;
  
      *left = *top = *width = *height = 0;
  
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (!childObj->documentFrameView())
          return E_FAIL;
  
      IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->elementRect()));
      *left = screenRect.x();
      *top = screenRect.y();
      *width = screenRect.width();
      *height = screenRect.height();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo)
  {
      if (!pvNavigatedTo)
          return E_POINTER;
  
      ::VariantInit(pvNavigatedTo);
  
      AccessibilityObject* childObj = 0;
  
      switch (direction) {
          case NAVDIR_DOWN:
          case NAVDIR_UP:
          case NAVDIR_LEFT:
          case NAVDIR_RIGHT:
              // These directions are not implemented, matching Mozilla and IE.
              return E_NOTIMPL;
          case NAVDIR_LASTCHILD:
          case NAVDIR_FIRSTCHILD:
              // MSDN states that navigating to first/last child can only be from self.
              if (vFromChild.lVal != CHILDID_SELF)
                  return E_INVALIDARG;
  
              if (!m_object)
                  return E_FAIL;
  
              if (direction == NAVDIR_FIRSTCHILD)
                  childObj = m_object->firstChild();
              else
                  childObj = m_object->lastChild();
              break;
          case NAVDIR_NEXT:
          case NAVDIR_PREVIOUS: {
              // Navigating to next and previous is allowed from self or any of our children.
              HRESULT hr = getAccessibilityObjectForChild(vFromChild, childObj);
              if (FAILED(hr))
                  return hr;
  
              if (direction == NAVDIR_NEXT)
                  childObj = childObj->nextSibling();
              else
                  childObj = childObj->previousSibling();
              break;
          }
          default:
              ASSERT_NOT_REACHED();
              return E_INVALIDARG;
      }
  
      if (!childObj)
          return S_FALSE;
  
      V_VT(pvNavigatedTo) = VT_DISPATCH;
      V_DISPATCH(pvNavigatedTo) = wrapper(childObj);
      V_DISPATCH(pvNavigatedTo)->AddRef();
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint)
  {
      if (!pvChildAtPoint)
          return E_POINTER;
  
      ::VariantInit(pvChildAtPoint);
  
      if (!m_object || !m_object->documentFrameView())
          return E_FAIL;
  
      IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y));
      AccessibilityObject* childObj = m_object->accessibilityHitTest(point);
  
      if (!childObj) {
          // If we did not hit any child objects, test whether the point hit us, and
          // report that.
          if (!m_object->boundingBoxRect().contains(point))
              return S_FALSE;
          childObj = m_object;
      }
  
      if (childObj == m_object) {
          V_VT(pvChildAtPoint) = VT_I4;
          V_I4(pvChildAtPoint) = CHILDID_SELF;
      } else {
          V_VT(pvChildAtPoint) = VT_DISPATCH;
          V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj));
          V_DISPATCH(pvChildAtPoint)->AddRef();
      }
      return S_OK;
  }
  
  HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild)
  {
      AccessibilityObject* childObj;
      HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
  
      if (FAILED(hr))
          return hr;
  
      if (!childObj->performDefaultAction())
          return S_FALSE;
  
      return S_OK;
  }
  
  // AccessibleBase
  String AccessibleBase::name() const
  {
      return m_object->nameForMSAA();
  }
  
  String AccessibleBase::value() const
  {
      return m_object->stringValueForMSAA();
  }
  
  static long MSAARole(AccessibilityRole role)
  {
      switch (role) {
          case WebCore::ButtonRole:
              return ROLE_SYSTEM_PUSHBUTTON;
          case WebCore::RadioButtonRole:
              return ROLE_SYSTEM_RADIOBUTTON;
          case WebCore::CheckBoxRole:
              return ROLE_SYSTEM_CHECKBUTTON;
          case WebCore::SliderRole:
              return ROLE_SYSTEM_SLIDER;
          case WebCore::TabGroupRole:
              return ROLE_SYSTEM_PAGETABLIST;
          case WebCore::TextFieldRole:
          case WebCore::TextAreaRole:
          case WebCore::EditableTextRole:
              return ROLE_SYSTEM_TEXT;
          case WebCore::ListMarkerRole:
          case WebCore::StaticTextRole:
              return ROLE_SYSTEM_STATICTEXT;
          case WebCore::OutlineRole:
              return ROLE_SYSTEM_OUTLINE;
          case WebCore::ColumnRole:
              return ROLE_SYSTEM_COLUMN;
          case WebCore::RowRole:
              return ROLE_SYSTEM_ROW;
          case WebCore::GroupRole:
              return ROLE_SYSTEM_GROUPING;
          case WebCore::ListRole:
          case WebCore::ListBoxRole:
          case WebCore::MenuListPopupRole:
              return ROLE_SYSTEM_LIST;
          case WebCore::TableRole:
              return ROLE_SYSTEM_TABLE;
          case WebCore::LinkRole:
          case WebCore::WebCoreLinkRole:
              return ROLE_SYSTEM_LINK;
          case WebCore::ImageMapRole:
          case WebCore::ImageRole:
              return ROLE_SYSTEM_GRAPHIC;
          case WebCore::MenuListOptionRole:
          case WebCore::ListItemRole:
          case WebCore::ListBoxOptionRole:
              return ROLE_SYSTEM_LISTITEM;
          case WebCore::PopUpButtonRole:
              return ROLE_SYSTEM_COMBOBOX;
          default:
              // This is the default role for MSAA.
              return ROLE_SYSTEM_CLIENT;
      }
  }
  
  long AccessibleBase::role() const
  {
      return MSAARole(m_object->roleValueForMSAA());
  }
  
  HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const
  {
      childObj = 0;
  
      if (!m_object)
          return E_FAIL;
  
      if (vChild.vt != VT_I4)
          return E_INVALIDARG;
  
      if (vChild.lVal == CHILDID_SELF)
          childObj = m_object;
      else if (vChild.lVal < 0) {
          // When broadcasting MSAA events, we negate the AXID and pass it as the
          // child ID.
          Document* document = m_object->document();
          if (!document)
              return E_FAIL;
  
          childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal);
      } else {
          size_t childIndex = static_cast<size_t>(vChild.lVal - 1);
  
          if (childIndex >= m_object->children().size())
              return E_FAIL;
          childObj = m_object->children().at(childIndex).get();
      }
  
      if (!childObj)
          return E_FAIL;
  
      return S_OK;
  }
  
  AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
  {
      AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper());
      if (!result)
          result = createInstance(obj);
      return result;
  }
  
  HRESULT AccessibleBase::isSameObject(IAccessibleComparable* other, BOOL* result)
  {
      COMPtr<AccessibleBase> otherAccessibleBase(Query, other);
      *result = (otherAccessibleBase == this || otherAccessibleBase->m_object == m_object);
      return S_OK;
  }
  