| // Copyright 2010-2015, 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 |
| #include <atlbase.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 |