blob: d8052c836c2803930fac66370d48d0ba49b0a382 [file] [log] [blame]
// Copyright 2010-2014, Google 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:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER 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 "win32/base/accessible_object.h"
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
// Workaround against KB813540
#include <atlbase_mozc.h>
#include <atlcom.h>
#include <string>
#include "base/util.h"
namespace mozc {
namespace win32 {
namespace {
using ::ATL::CComBSTR;
using ::ATL::CComPtr;
using ::ATL::CComQIPtr;
using ::ATL::CComVariant;
string UTF16ToUTF8(const wstring &str) {
string utf8;
Util::WideToUTF8(str, &utf8);
return utf8;
}
string BSTRToUTF8(const BSTR &bstr) {
if (bstr == nullptr) {
return "";
}
return UTF16ToUTF8(wstring(bstr, ::SysStringLen(bstr)));
}
string RoleToString(const CComVariant &role) {
if (role.vt == VT_I4) {
switch (role.lVal) {
case ROLE_SYSTEM_TITLEBAR:
return "ROLE_SYSTEM_TITLEBAR";
case ROLE_SYSTEM_MENUBAR:
return "ROLE_SYSTEM_MENUBAR";
case ROLE_SYSTEM_SCROLLBAR:
return "ROLE_SYSTEM_SCROLLBAR";
case ROLE_SYSTEM_GRIP:
return "ROLE_SYSTEM_GRIP";
case ROLE_SYSTEM_SOUND:
return "ROLE_SYSTEM_SOUND";
case ROLE_SYSTEM_CURSOR:
return "ROLE_SYSTEM_CURSOR";
case ROLE_SYSTEM_CARET:
return "ROLE_SYSTEM_CARET";
case ROLE_SYSTEM_ALERT:
return "ROLE_SYSTEM_ALERT";
case ROLE_SYSTEM_WINDOW:
return "ROLE_SYSTEM_WINDOW";
case ROLE_SYSTEM_CLIENT:
return "ROLE_SYSTEM_CLIENT";
case ROLE_SYSTEM_MENUPOPUP:
return "ROLE_SYSTEM_MENUPOPUP";
case ROLE_SYSTEM_MENUITEM:
return "ROLE_SYSTEM_MENUITEM";
case ROLE_SYSTEM_TOOLTIP:
return "ROLE_SYSTEM_TOOLTIP";
case ROLE_SYSTEM_APPLICATION:
return "ROLE_SYSTEM_APPLICATION";
case ROLE_SYSTEM_DOCUMENT:
return "ROLE_SYSTEM_DOCUMENT";
case ROLE_SYSTEM_PANE:
return "ROLE_SYSTEM_PANE";
case ROLE_SYSTEM_CHART:
return "ROLE_SYSTEM_CHART";
case ROLE_SYSTEM_DIALOG:
return "ROLE_SYSTEM_DIALOG";
case ROLE_SYSTEM_BORDER:
return "ROLE_SYSTEM_BORDER";
case ROLE_SYSTEM_GROUPING:
return "ROLE_SYSTEM_GROUPING";
case ROLE_SYSTEM_SEPARATOR:
return "ROLE_SYSTEM_SEPARATOR";
case ROLE_SYSTEM_TOOLBAR:
return "ROLE_SYSTEM_TOOLBAR";
case ROLE_SYSTEM_STATUSBAR:
return "ROLE_SYSTEM_STATUSBAR";
case ROLE_SYSTEM_TABLE:
return "ROLE_SYSTEM_TABLE";
case ROLE_SYSTEM_COLUMNHEADER:
return "ROLE_SYSTEM_COLUMNHEADER";
case ROLE_SYSTEM_ROWHEADER:
return "ROLE_SYSTEM_ROWHEADER";
case ROLE_SYSTEM_COLUMN:
return "ROLE_SYSTEM_COLUMN";
case ROLE_SYSTEM_ROW:
return "ROLE_SYSTEM_ROW";
case ROLE_SYSTEM_CELL:
return "ROLE_SYSTEM_CEL";
case ROLE_SYSTEM_LINK:
return "ROLE_SYSTEM_LINK";
case ROLE_SYSTEM_HELPBALLOON:
return "ROLE_SYSTEM_HELPBALLOON";
case ROLE_SYSTEM_CHARACTER:
return "ROLE_SYSTEM_CHARACTER";
case ROLE_SYSTEM_LIST:
return "ROLE_SYSTEM_LIST";
case ROLE_SYSTEM_LISTITEM:
return "ROLE_SYSTEM_LISTITEM";
case ROLE_SYSTEM_OUTLINE:
return "ROLE_SYSTEM_OUTLINE";
case ROLE_SYSTEM_OUTLINEITEM:
return "ROLE_SYSTEM_OUTLINEITEM";
case ROLE_SYSTEM_PAGETAB:
return "ROLE_SYSTEM_PAGETAB";
case ROLE_SYSTEM_PROPERTYPAGE:
return "ROLE_SYSTEM_PROPERTYPAGE";
case ROLE_SYSTEM_INDICATOR:
return "ROLE_SYSTEM_INDICATOR";
case ROLE_SYSTEM_GRAPHIC:
return "ROLE_SYSTEM_GRAPHIC";
case ROLE_SYSTEM_STATICTEXT:
return "ROLE_SYSTEM_STATICTEXT";
case ROLE_SYSTEM_TEXT:
return "ROLE_SYSTEM_TEXT";
case ROLE_SYSTEM_PUSHBUTTON:
return "ROLE_SYSTEM_PUSHBUTTON";
case ROLE_SYSTEM_CHECKBUTTON:
return "ROLE_SYSTEM_CHECKBUTTON";
case ROLE_SYSTEM_RADIOBUTTON:
return "ROLE_SYSTEM_RADIOBUTTON";
case ROLE_SYSTEM_COMBOBOX:
return "ROLE_SYSTEM_COMBOBOX";
case ROLE_SYSTEM_DROPLIST:
return "ROLE_SYSTEM_DROPLIST";
case ROLE_SYSTEM_PROGRESSBAR:
return "ROLE_SYSTEM_PROGRESSBAR";
case ROLE_SYSTEM_DIAL:
return "ROLE_SYSTEM_DIAL";
case ROLE_SYSTEM_HOTKEYFIELD:
return "ROLE_SYSTEM_HOTKEYFIELD";
case ROLE_SYSTEM_SLIDER:
return "ROLE_SYSTEM_SLIDER";
case ROLE_SYSTEM_SPINBUTTON:
return "ROLE_SYSTEM_SPINBUTTON";
case ROLE_SYSTEM_DIAGRAM:
return "ROLE_SYSTEM_DIAGRAM";
case ROLE_SYSTEM_ANIMATION:
return "ROLE_SYSTEM_ANIMATION";
case ROLE_SYSTEM_EQUATION:
return "ROLE_SYSTEM_EQUATION";
case ROLE_SYSTEM_BUTTONDROPDOWN:
return "ROLE_SYSTEM_BUTTONDROPDOWN";
case ROLE_SYSTEM_BUTTONMENU:
return "ROLE_SYSTEM_BUTTONMENU";
case ROLE_SYSTEM_BUTTONDROPDOWNGRID:
return "ROLE_SYSTEM_BUTTONDROPDOWNGRID";
case ROLE_SYSTEM_WHITESPACE:
return "ROLE_SYSTEM_WHITESPACE";
case ROLE_SYSTEM_PAGETABLIST:
return "ROLE_SYSTEM_PAGETABLIST";
case ROLE_SYSTEM_CLOCK:
return "ROLE_SYSTEM_CLOCK";
case ROLE_SYSTEM_SPLITBUTTON:
return "ROLE_SYSTEM_SPLITBUTTON";
case ROLE_SYSTEM_IPADDRESS:
return "ROLE_SYSTEM_IPADDRESS";
case ROLE_SYSTEM_OUTLINEBUTTON:
return "ROLE_SYSTEM_OUTLINEBUTTON";
default:
return "";
}
} else if (role.vt == VT_BSTR) {
return BSTRToUTF8(role.bstrVal);
} else {
return "";
}
}
AccessibleObjectInfo GetInfo(const ATL::CComVariant &role,
const string &name,
const string &value) {
AccessibleObjectInfo info;
info.role = RoleToString(role);
info.is_builtin_role = (role.vt == VT_I4);
info.name = name;
info.value = value;
return info;
}
CComVariant GetChildId(int32 child_id) {
CComVariant variant;
variant.vt = VT_I4;
variant.lVal = child_id;
return variant;
}
} // namespace
AccessibleObject::AccessibleObject()
: child_id_(CHILDID_SELF),
valid_(false) {}
AccessibleObject::AccessibleObject(CComPtr<IAccessible> container)
: container_(container),
child_id_(CHILDID_SELF),
valid_(container != nullptr) {
}
AccessibleObject::AccessibleObject(CComPtr<IAccessible> container,
int32 child_id)
: container_(container),
child_id_(child_id),
valid_(container != nullptr) {
}
AccessibleObjectInfo AccessibleObject::GetInfo() const {
AccessibleObjectInfo info;
const auto child = GetChildId(child_id_);
{
CComVariant role;
if (SUCCEEDED(container_->get_accRole(child, &role))) {
info.is_builtin_role = (role.vt == VT_I4);
info.role = RoleToString(role);
}
}
{
CComBSTR bstr;
if (SUCCEEDED(container_->get_accName(child, &bstr))) {
info.name = BSTRToUTF8(bstr);
}
}
{
CComBSTR bstr;
if (SUCCEEDED(container_->get_accValue(child, &bstr))) {
info.value = BSTRToUTF8(bstr);
}
}
return info;
}
vector<AccessibleObject> AccessibleObject::GetChildren() const {
vector<AccessibleObject> result;
if (!container_) {
result;
}
LONG num_children = 0;
if (FAILED(container_->get_accChildCount(&num_children))) {
return result;
}
if (num_children == 0) {
return result;
}
vector<CComVariant> buffer;
buffer.resize(num_children);
LONG num_fetched = 0;
if (FAILED(::AccessibleChildren(
container_, 0, num_children, &buffer[0], &num_fetched))) {
return result;
}
buffer.resize(num_fetched);
for (size_t i = 0; i < num_fetched; ++i) {
const auto &element = buffer[i];
if (element.vt == VT_DISPATCH) {
CComQIPtr<IAccessible> accesible(element.pdispVal);
if (accesible != nullptr) {
result.push_back(AccessibleObject(accesible));
}
} else if (element.vt == VT_I4) {
result.push_back(AccessibleObject(container_, element.lVal));
} else {
// not supported.
}
}
return result;
}
AccessibleObject AccessibleObject::GetParent() const {
if (child_id_ != CHILDID_SELF) {
// not supported.
return AccessibleObject();
}
CComPtr<IDispatch> dispatch;
if (FAILED(container_->get_accParent(&dispatch))) {
return AccessibleObject();
}
return AccessibleObject(CComQIPtr<IAccessible>(dispatch));
}
AccessibleObject AccessibleObject::GetFocus() const {
CComVariant variant;
if (FAILED(container_->get_accFocus(&variant))) {
return AccessibleObject();
}
if (variant.vt == VT_I4) {
return AccessibleObject(container_, variant.lVal);
} else if (variant.vt == VT_DISPATCH) {
return AccessibleObject(CComQIPtr<IAccessible>(variant.pdispVal));
}
return AccessibleObject();
}
bool AccessibleObject::GetWindowHandle(HWND *window_handle) const {
if (window_handle == nullptr) {
return false;
}
*window_handle = nullptr;
if (child_id_ != CHILDID_SELF) {
// not supported.
return false;
}
return SUCCEEDED(::WindowFromAccessibleObject(container_, window_handle));
}
bool AccessibleObject::GetProcessId(DWORD *process_id) const {
if (process_id == nullptr) {
return false;
}
*process_id = 0;
HWND window_handle = nullptr;
if (!GetWindowHandle(&window_handle)) {
return false;
}
if (::GetWindowThreadProcessId(window_handle, process_id) == 0) {
return false;
}
return true;
}
bool AccessibleObject::IsValid() const {
return valid_;
}
// static
AccessibleObject AccessibleObject::FromWindow(HWND window_handle) {
if (::IsWindow(window_handle) == FALSE) {
return AccessibleObject();
}
CComPtr<IAccessible> accesible;
if (FAILED(::AccessibleObjectFromWindow(
window_handle, OBJID_WINDOW, __uuidof(IAccessible),
reinterpret_cast<void **>(&accesible)))) {
return AccessibleObject();
}
return AccessibleObject(accesible);
}
} // namespace win32
} // namespace mozc