summaryrefslogtreecommitdiffstats
path: root/chrome/test/accessibility/accessibility_util.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/test/accessibility/accessibility_util.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/accessibility/accessibility_util.cc')
-rw-r--r--chrome/test/accessibility/accessibility_util.cc620
1 files changed, 620 insertions, 0 deletions
diff --git a/chrome/test/accessibility/accessibility_util.cc b/chrome/test/accessibility/accessibility_util.cc
new file mode 100644
index 0000000..43a79a8
--- /dev/null
+++ b/chrome/test/accessibility/accessibility_util.cc
@@ -0,0 +1,620 @@
+// Copyright 2008, 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 "accessibility_util.h"
+#include "constants.h"
+#include "chrome/common/win_util.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/browser/xp_frame.h"
+#include "chrome/browser/vista_frame.h"
+#include "generated_resources.h"
+
+VARIANT g_var_self = {VT_I4, CHILDID_SELF};
+
+HWND GetChromeBrowserWnd(IAccessible** ppi_access) {
+ HRESULT hr = S_OK;
+ HWND hwnd = NULL;
+ BSTR str_name;
+ std::wstring str_role;
+
+ const std::wstring product_name = l10n_util::GetString(IDS_PRODUCT_NAME);
+
+ // Get Chrome window handle.
+ if (win_util::ShouldUseVistaFrame()) {
+ hwnd = FindWindow(VISTA_FRAME_CLASSNAME, NULL);
+ } else {
+ hwnd = FindWindow(XP_FRAME_CLASSNAME, NULL);
+ }
+
+ if (NULL == hwnd) {
+ if (ppi_access)
+ *ppi_access = NULL;
+ return hwnd;
+ }
+
+ // Get accessibility object for Chrome, only if requested.
+ if (!ppi_access) {
+ return hwnd;
+ }
+ *ppi_access = NULL;
+
+ // Get accessibility object for Chrome Main Window. If failed to get it,
+ // return only window handle.
+ IAccessible *pi_acc_root_win = NULL;
+ hr = AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
+ reinterpret_cast<void**>
+ (&pi_acc_root_win));
+ if ((S_OK != hr) || !pi_acc_root_win) {
+ return hwnd;
+ }
+
+
+ // Confirm if it is Chrome window using it's accessibility object's
+ // Name and Role property. If it's not the desired object, return only
+ // window handle.
+ hr = pi_acc_root_win->get_accName(g_var_self, &str_name);
+ if ((S_OK != hr) || (!str_name) ||
+ (0 != _wcsicmp(str_name, product_name.c_str())) ) {
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+ str_role = GetRole(pi_acc_root_win);
+ if (0 != str_role.compare(BROWSER_WIN_ROLE)) {
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+
+ // Get accessibility object for Chrome Window. If failed to get it,
+ // return only window handle.
+ INT64 child_cnt = GetChildCount(pi_acc_root_win);
+ VARIANT *var_array_child =
+ reinterpret_cast<VARIANT*>(calloc(size_t(child_cnt),
+ sizeof(VARIANT)));
+
+ if (!var_array_child) {
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+
+ hr = GetChildrenArray(pi_acc_root_win, var_array_child);
+ if (S_OK != hr) {
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+
+ // Fetch desired child (Chrome window) of Chrome Main Window.
+ IAccessible *pi_acc_app = NULL;
+ GetChildObject(pi_acc_root_win, var_array_child[CHROME_APP_ACC_INDEX],
+ &pi_acc_app);
+ if (!pi_acc_app) {
+ CHK_RELEASE(pi_acc_app);
+ return hwnd;
+ }
+
+ // Confirm if it is Chrome application using it's accessibility object's
+ // Name and Role property. If it's not the desired object, return only
+ // window handle.
+ hr = pi_acc_app->get_accName(g_var_self, &str_name);
+ if ((S_OK != hr) || (!str_name) ||
+ (0 != _wcsicmp(str_name, product_name.c_str())) ) {
+ CHK_RELEASE(pi_acc_app);
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+ str_role = GetRole(pi_acc_app);
+ if (0 != str_role.compare(BROWSER_APP_ROLE)) {
+ CHK_RELEASE(pi_acc_app);
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+
+ // Get accessibility object for Chrome Client. If failed, return only
+ // window handle.
+ hr = GetChildrenArray(pi_acc_app, var_array_child);
+ if (S_OK != hr) {
+ CHK_RELEASE(pi_acc_app);
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+ }
+
+ // Chrome Window has only one child which is Chrome Client.
+ GetChildObject(pi_acc_app, var_array_child[CHROME_CLIENT_ACC_INDEX],
+ ppi_access);
+
+ // Confirm if it is Chrome client using it's accessibility object's Name
+ // and Role property. If it's not the desired object, return only window
+ // handle.
+ hr = (*ppi_access)->get_accName(g_var_self, &str_name);
+ if ((S_OK != hr) || (!str_name) ||
+ (0 != _wcsicmp(str_name, product_name.c_str())) ) {
+ CHK_RELEASE(*ppi_access);
+ }
+ str_role = GetRole(*ppi_access);
+ if (0 != str_role.compare(BROWSER_CLIENT_ROLE)) {
+ CHK_RELEASE(*ppi_access);
+ }
+
+ CHK_RELEASE(pi_acc_app);
+ CHK_RELEASE(pi_acc_root_win);
+ return hwnd;
+}
+
+HRESULT GetChildWndOf(std::wstring parent_name, unsigned int child_index,
+ IAccessible** ppi_access, VARIANT* child_var_id) {
+ HRESULT hr = S_OK;
+
+ // Validate input and initialize.
+ if (!ppi_access && !child_var_id)
+ return E_INVALIDARG;
+ if (ppi_access)
+ *ppi_access = NULL;
+ if (child_var_id)
+ VariantInit(child_var_id);
+
+ // Get accessibility object and window handle for Chrome parent.
+ IAccessible *pi_parent = NULL;
+ if (0 == parent_name.compare(BROWSER_STR))
+ GetChromeBrowserWnd(&pi_parent);
+ if (0 == parent_name.compare(TOOLBAR_STR))
+ GetToolbarWnd(&pi_parent);
+ if (0 == parent_name.compare(TABSTRIP_STR))
+ GetTabStripWnd(&pi_parent);
+
+ if (!pi_parent)
+ return E_FAIL;
+
+ // Validate child index.
+ INT64 child_cnt = GetChildCount(pi_parent);
+ if (child_index >= child_cnt) {
+ CHK_RELEASE(pi_parent);
+ VariantClear(child_var_id);
+ return E_INVALIDARG;
+ }
+
+ // Get array of child items of parent object.
+ VARIANT *var_array_child =
+ reinterpret_cast<VARIANT*>(calloc(size_t(child_cnt), sizeof(VARIANT)));
+ if (var_array_child) {
+ hr = GetChildrenArray(pi_parent, var_array_child);
+ if (S_OK == hr) {
+ // Fetch Tabstrip which is child_index'th child of parent object.
+ if (ppi_access) {
+ hr = GetChildObject(pi_parent, var_array_child[child_index],
+ ppi_access);
+ }
+ if (child_var_id) {
+ VariantCopy(child_var_id, var_array_child+child_index);
+ }
+ }
+ free(var_array_child);
+ }
+
+ CHK_RELEASE(pi_parent);
+ return hr;
+}
+
+HRESULT GetTabStripWnd(IAccessible** ppi_access) {
+ return GetChildWndOf(BROWSER_STR, TABSTRIP_ACC_INDEX, ppi_access, NULL);
+}
+
+HRESULT GetToolbarWnd(IAccessible** ppi_access) {
+ return GetChildWndOf(BROWSER_STR, TOOLBAR_ACC_INDEX, ppi_access, NULL);
+}
+
+HRESULT GetBrowserMinimizeButton(IAccessible** ppi_access,
+ VARIANT* child_var_id) {
+ return GetChildWndOf(BROWSER_STR, CHROME_MIN_ACC_INDEX, ppi_access,
+ child_var_id);
+}
+
+HRESULT GetBrowserMaximizeButton(IAccessible** ppi_access,
+ VARIANT* child_var_id) {
+ return GetChildWndOf(BROWSER_STR, CHROME_MAX_ACC_INDEX, ppi_access,
+ child_var_id);
+}
+
+HRESULT GetBrowserRestoreButton(IAccessible** ppi_access,
+ VARIANT* child_var_id) {
+ return GetChildWndOf(BROWSER_STR, CHROME_RESTORE_ACC_INDEX, ppi_access,
+ child_var_id);
+}
+
+HRESULT GetBrowserCloseButton(IAccessible** ppi_access,
+ VARIANT* child_var_id) {
+ return GetChildWndOf(BROWSER_STR, CHROME_CLOSE_ACC_INDEX, ppi_access,
+ child_var_id);
+}
+
+HRESULT GetStarButton(IAccessible** ppi_access, VARIANT* child_var_id) {
+ return GetChildWndOf(TOOLBAR_STR, STAR_BTN_INDEX, ppi_access, child_var_id);
+}
+
+HRESULT GetBackButton(IAccessible** ppi_access, VARIANT* child_var_id) {
+ return GetChildWndOf(TOOLBAR_STR, BACK_BTN_INDEX, ppi_access, child_var_id);
+}
+
+HRESULT GetForwardButton(IAccessible** ppi_access, VARIANT* child_var_id) {
+ return GetChildWndOf(TOOLBAR_STR, FORWARD_BTN_INDEX, ppi_access,
+ child_var_id);
+}
+
+HWND GetAddressBarWnd(IAccessible** ppi_access) {
+ HWND hwnd = NULL;
+ HWND hwnd_addr_bar = NULL;
+
+ // // Initialize, if requested.
+ if (ppi_access) {
+ *ppi_access = NULL;
+ }
+
+ // Get window handle for Chrome Browser.
+ hwnd = GetChromeBrowserWnd(NULL);
+ if (NULL != hwnd) {
+ // Get AddressBar/OmniBox (edit box) window handle.
+ hwnd_addr_bar = FindWindowEx(hwnd, 0, CHROME_AUTOCOMPLETE_EDIT, NULL);
+
+ // Get accessibility object for address bar, if requested.
+ if (ppi_access && hwnd_addr_bar) {
+ AccessibleObjectFromWindow(hwnd_addr_bar, OBJID_WINDOW, IID_IAccessible,
+ reinterpret_cast<void**>(ppi_access));
+ }
+ }
+
+ return hwnd_addr_bar;
+}
+
+HWND GetFindTextWnd(IAccessible** ppi_access) {
+ HWND hwnd = NULL;
+ HWND hwnd_find = NULL;
+
+ // Initialize, if requested.
+ if (ppi_access) {
+ *ppi_access = NULL;
+ }
+
+ // Get window handle for Chrome Browser.
+ hwnd = GetChromeBrowserWnd(NULL);
+ if (NULL != hwnd) {
+ // Get handle of a window, which is contains edit box for Find string.
+ hwnd_find = FindWindowEx(hwnd, 0, CHROME_HWND_VIEW_CONTAINER, NULL);
+
+ // Get accessibility object, if requested.
+ if (ppi_access && hwnd_find) {
+ AccessibleObjectFromWindow(hwnd_find, OBJID_WINDOW, IID_IAccessible,
+ reinterpret_cast<void**>(ppi_access));
+ }
+ }
+
+ return hwnd_find;
+}
+
+HWND GetAuthWnd(IAccessible** ppi_access) {
+ HWND hwnd = NULL;
+ HWND hwnd_tab = NULL;
+ HWND hwnd_auth = NULL;
+
+ // Initialize, if requested.
+ if (ppi_access) {
+ *ppi_access = NULL;
+ }
+
+ // Get window handle for Chrome Browser.
+ hwnd = GetChromeBrowserWnd(NULL);
+ if (NULL != hwnd) {
+ // Get window handle for tab.
+ hwnd_tab = FindWindowEx(hwnd, 0, CHROME_TAB_CONTENTS, NULL);
+ if (!hwnd_tab)
+ return hwnd_auth;
+
+ // Get handle for Authentication window.
+ hwnd_auth = FindWindowEx(hwnd_tab, 0, CHROME_HWND_VIEW_CONTAINER,
+ AUTH_TITLE);
+
+ // Get accessibility object, if requested.
+ if (ppi_access && hwnd_auth) {
+ AccessibleObjectFromWindow(hwnd_auth, OBJID_WINDOW, IID_IAccessible,
+ reinterpret_cast<void**>(ppi_access));
+ }
+ }
+
+ return hwnd_auth;
+}
+
+HRESULT GetChildObject(IAccessible* pi_access, VARIANT var_child,
+ IAccessible** ppi_child_access) {
+ HRESULT hr = S_OK;
+ IDispatch *p_dispatch = NULL;
+
+ // Validate input.
+ if ( (pi_access == NULL) ||
+ (ppi_child_access == NULL) ) {
+ return E_INVALIDARG;
+ }
+
+ // Check the child type and fetch object accordingly.
+ if (var_child.vt == VT_DISPATCH) {
+ var_child.pdispVal->
+ QueryInterface(IID_IAccessible,
+ reinterpret_cast<void**>(ppi_child_access));
+ } else if (var_child.vt == VT_I4) {
+ hr = pi_access->get_accChild(var_child, &p_dispatch);
+ if ( (hr == S_OK) &&
+ (p_dispatch != NULL) ) {
+ p_dispatch->QueryInterface(IID_IAccessible,
+ reinterpret_cast<void**>(ppi_child_access));
+ CHK_RELEASE(p_dispatch);
+ }
+ }
+
+ return hr;
+}
+
+HRESULT GetParentObject(IAccessible* pi_access,
+ IAccessible** ppi_parent_access) {
+ HRESULT hr = S_OK;
+ IDispatch *p_dispatch = NULL;
+
+ // Validate input.
+ if ( (pi_access == NULL) ||
+ (ppi_parent_access == NULL) ) {
+ return E_INVALIDARG;
+ }
+
+ // Fetch parent object.
+ hr = pi_access->get_accParent(&p_dispatch);
+ if ( (hr == S_OK) &&
+ (p_dispatch != NULL) ) {
+ p_dispatch->QueryInterface(IID_IAccessible,
+ reinterpret_cast<void**>(ppi_parent_access));
+ CHK_RELEASE(p_dispatch);
+ }
+
+ return hr;
+}
+
+INT64 GetChildCount(IAccessible* pi_access) {
+ HRESULT hr = S_OK;
+ long child_cnt = 0;
+
+ // Validate input. Object can have 0 children. So return -1 on invalid input.
+ if (pi_access == NULL) {
+ return -1;
+ }
+
+ // Get child count.
+ pi_access->get_accChildCount(&child_cnt);
+ return child_cnt;
+}
+
+HRESULT GetChildrenArray(IAccessible* pi_access, VARIANT* var_array_child) {
+ HRESULT hr = S_OK;
+ INT64 child_start = 0;
+ long child_obtained = 0;
+ INT64 child_cnt = GetChildCount(pi_access);
+
+ // Validate input.
+ if ((pi_access == NULL) || (var_array_child == NULL)) {
+ return E_INVALIDARG;
+ }
+
+ // Validate every item and initialize it.
+ int i = 0;
+ for (; (i < child_cnt) && (var_array_child+i); i++) {
+ VariantInit(var_array_child+i);
+ }
+
+ // If all items in array are not initialized, return error.
+ if (i != child_cnt) {
+ return E_INVALIDARG;
+ }
+
+ // Get IDs of child items.
+ AccessibleChildren(pi_access,
+ LONG(child_start),
+ LONG(child_cnt),
+ var_array_child,
+ &child_obtained);
+ return hr;
+}
+
+HRESULT ActivateWnd(IAccessible *pi_access, HWND hwnd) {
+ HRESULT hr = S_OK;
+
+ // Select and focus the object, if accessibility object is specified.
+ if (pi_access) {
+ hr = pi_access->accSelect(SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION,
+ g_var_self);
+ }
+
+ // Send message to window, if window handle is specified.
+ if (hwnd) {
+ SetActiveWindow(hwnd);
+ }
+
+ return hr;
+}
+
+BSTR GetTabName(INT64 tab_index) {
+ HRESULT hr = S_OK;
+ BSTR str_name;
+
+ // Validate tab index specified.
+ if (tab_index < 1)
+ return NULL;
+
+ // Get accessibility object for Tabstrip.
+ IAccessible *pi_acc_strip = NULL;
+ GetTabStripWnd(&pi_acc_strip);
+
+ // Get Tab from Tabstrip and return it's Name.
+ if (pi_acc_strip) {
+ INT64 child_cnt = GetChildCount(pi_acc_strip);
+ VARIANT *var_array_child =
+ reinterpret_cast<VARIANT*>(calloc(size_t(child_cnt), sizeof(VARIANT)));
+ if (var_array_child) {
+ // Get tab object. tab_index = index in child array, because first child
+ // in tabstrip is '+' button.
+ hr = GetChildrenArray(pi_acc_strip, var_array_child);
+ if (S_OK == hr) {
+ IAccessible *pi_access_temp = NULL;
+ hr = GetChildObject(pi_acc_strip, var_array_child[tab_index],
+ &pi_access_temp);
+ if ((S_OK == hr) &&
+ (var_array_child[tab_index].vt == VT_DISPATCH) &&
+ (pi_access_temp) ) {
+ hr = pi_access_temp->get_accName(g_var_self, &str_name);
+ } else if (var_array_child[tab_index].vt == VT_I4) {
+ hr = pi_acc_strip->get_accName(var_array_child[1], &str_name);
+ }
+ CHK_RELEASE(pi_acc_strip);
+ return str_name;
+ }
+ }
+
+ CHK_RELEASE(pi_acc_strip);
+ }
+
+ return NULL;
+}
+
+INT64 GetTabCnt() {
+ // Get accessibility object for Tabstrip.
+ IAccessible *pi_acc_strip = NULL;
+ GetTabStripWnd(&pi_acc_strip);
+
+ // If Tabstrip is invalid, return -1, to indicate error.
+ if (!pi_acc_strip) {
+ return -1;
+ }
+
+ // Get child count.
+ INT64 child_cnt = 0;
+ if (pi_acc_strip) {
+ child_cnt = GetChildCount(pi_acc_strip);
+ CHK_RELEASE(pi_acc_strip);
+ }
+
+ // Don't count 1st child as it is '+' button.
+ return (child_cnt-1);
+}
+
+std::wstring GetName(IAccessible* pi_access, VARIANT child) {
+ HRESULT hr = S_OK;
+
+ // Validate input.
+ if (NULL == pi_access) {
+ return std::wstring();
+ }
+
+ // Get Name.
+ BSTR name;
+ hr = pi_access->get_accName(child, &name);
+ if (S_OK != hr)
+ return std::wstring();
+
+ return std::wstring(name);
+}
+
+std::wstring GetRole(IAccessible* pi_access, VARIANT child) {
+ HRESULT hr = S_OK;
+ LPTSTR role_str = NULL;
+
+ // Validate input.
+ if (NULL == pi_access) {
+ return std::wstring();
+ }
+
+ // Get Role.
+ VARIANT role;
+ VariantInit(&role);
+ hr = pi_access->get_accRole(child, &role);
+ if (S_OK != hr || VT_I4 != role.vt) {
+ VariantClear(&role);
+ return std::wstring();
+ }
+
+ // Get Role string.
+ unsigned int role_length = GetRoleText(role.lVal, NULL, 0);
+ role_str = (LPTSTR)calloc(role_length + 1, sizeof(TCHAR));
+ if (role_str)
+ GetRoleText(role.lVal, role_str, role_length + 1);
+
+ VariantClear(&role);
+ return std::wstring(role_str);
+}
+
+std::wstring GetState(IAccessible* pi_access, VARIANT child) {
+ HRESULT hr = S_OK;
+ LPTSTR state_str = NULL;
+ std::wstring complete_state;
+
+ // Validate input.
+ if (NULL == pi_access) {
+ return std::wstring();
+ }
+
+ // Get State.
+ VARIANT state;
+ VariantInit(&state);
+ hr = pi_access->get_accState(child, &state);
+ if (S_OK != hr || VT_I4 != state.vt) {
+ VariantClear(&state);
+ return std::wstring();
+ }
+
+ // Treat the "normal" state separately.
+ if (state.vt == 0) {
+ unsigned int state_length = GetStateText(state.lVal, NULL, 0);
+ state_str = (LPTSTR)calloc(state_length + 1, sizeof(TCHAR));
+ if (state_str) {
+ GetStateText(state.lVal, state_str, state_length + 1);
+ complete_state = std::wstring(state_str);
+ }
+ } else {
+ // Number of bits.
+ UINT bit_cnt = 32;
+ // Convert state flags to comma separated list.
+ for (DWORD dwStateBit = 0x80000000; bit_cnt; bit_cnt--, dwStateBit >>= 1) {
+ if (state.lVal & dwStateBit) {
+ unsigned int state_length = GetStateText(dwStateBit, NULL, 0);
+ state_str = (LPTSTR)calloc(state_length + 1, sizeof(TCHAR));
+ if (state_str) {
+ GetStateText(dwStateBit, state_str, state_length + 1);
+ if (complete_state.length() > 0)
+ complete_state.append(L", ");
+ complete_state.append(state_str);
+ free(state_str);
+ }
+ }
+ }
+ }
+
+ VariantClear(&state);
+ return complete_state;
+}