// 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 "chrome/test/accessibility/browser_impl.h" #include #include "chrome/test/accessibility/accessibility_util.h" #include "chrome/test/accessibility/keyboard_util.h" #include "chrome/test/accessibility/registry_util.h" bool CBrowserImpl::Launch(void) { // TODO: Check if chrome already running. BSTR chrome_path = SysAllocString(GetChromeExePath()); BOOL bool_return = FALSE; // Initialize and fill up structure. SHELLEXECUTEINFO shell_execute_info; memset(&shell_execute_info, 0, sizeof(SHELLEXECUTEINFO)); shell_execute_info.cbSize = sizeof(SHELLEXECUTEINFO); // To get Process handle. shell_execute_info.fMask = SEE_MASK_NOCLOSEPROCESS; shell_execute_info.nShow = SW_SHOW; shell_execute_info.lpFile = reinterpret_cast(malloc(sizeof(TCHAR) * SysStringLen(chrome_path))); _tcscpy_s((TCHAR*)(shell_execute_info.lpFile), SysStringLen(chrome_path), chrome_path); // Execute. bool_return = ShellExecuteEx(&shell_execute_info); if (bool_return && (INT64(shell_execute_info.hInstApp) > 32) ) { // TODO: Maintain instance and process handle. // Maintain active tab index. SetActiveTabIndex(1); // Create initial tab collection. UpdateTabCollection(); // Chrome launched. return true; } return false; } bool CBrowserImpl::Quit(void) { // Cleanup. EraseTabCollection(); // Send close message to browser window. HWND hwnd = GetChromeBrowserWnd(NULL); if (!hwnd) return false; SendMessage(hwnd, WM_CLOSE, 0, 0); return true; } bool CBrowserImpl::ActivateTab(const INT64 index) { // Validate index specified. if (index < 1) { return false; } // Goto next tab till focused at desired tab. // TODO: Change implementation when DoDefaultAction() for Tab is exported. while (active_tab_index_ != index) { GoToNextTab(NULL); } return true; } bool CBrowserImpl::GetActiveTabURL(BSTR* url) { // Validate input. if (!url) return false; // TODO: Implement. return true; } bool CBrowserImpl::GetActiveTabTitle(BSTR* title) { if (!title) return false; BSTR tab_title = SysAllocString(GetTabName(active_tab_index_)); *title = SysAllocString(tab_title); return true; } bool CBrowserImpl::GetActiveTabIndex(INT64* index) { if (!index) return false; *index = active_tab_index_; return true; } void CBrowserImpl::SetActiveTabIndex(INT64 index) { if ((index >= MIN_TAB_INDEX_DIGIT) && (index <= GetTabCnt())) active_tab_index_ = index; return; } bool CBrowserImpl::GetActiveTab(CTabImpl** tab) { return GetTab(active_tab_index_, tab); } bool CBrowserImpl::GetTabCount(INT64* count) { if (!count) return false; *count = GetTabCnt(); return true; } bool CBrowserImpl::GetBrowserProcessCount(INT64* count) { if (!count) return false; // TODO: Add your implementation code here return true; } bool CBrowserImpl::GetBrowserTitle(BSTR* title) { if (!title) return false; HWND hwnd = GetChromeBrowserWnd(NULL); if (!hwnd) return false; int text_length = GetWindowTextLength(hwnd); *title = SysAllocStringLen(NULL, text_length); GetWindowText(hwnd, *title, text_length); return true; } bool CBrowserImpl::AddTab(CTabImpl** tab) { // Add new tab. HWND hwnd = GetChromeBrowserWnd(NULL); if (!hwnd) return false; ClickKey(hwnd, VK_CONTROL, 'T'); // Update active tab index. INT64 new_tab_index = GetTabCnt(); if (-1 == new_tab_index) return false; SetActiveTabIndex(new_tab_index); // Fill object. CTabImpl *new_tab = new CTabImpl(); if (!new_tab) return false; ChromeTab* tab_data = new_tab->InitTabData(); new_tab->PutIndex(new_tab_index); new_tab->PutTitle(GetTabName(new_tab_index)); new_tab->SetBrowser(this); // Update tab collection. tab_collection_.push_back(tab_data); // Create tab object, if requested. if (tab) *tab = new_tab; return true; } bool CBrowserImpl::GetTab(const INT64 index, CTabImpl** tab) { // Create tab object, if requested. if (!tab) return false; if (index > GetTabCnt()) return false; *tab = new CTabImpl(); if (!*tab) return false; // Fill object. ChromeTab* tab_data = (*tab)->InitTabData(); (*tab)->PutIndex(index); (*tab)->PutTitle(GetTabName(index)); (*tab)->SetBrowser(this); return true; } bool CBrowserImpl::GoToTab(const INT64 index, CTabImpl** tab) { // Validate input. if (index > MAX_TAB_INDEX_DIGIT) return false; // Stay on current tab, if index doesnot exist. if ((0 == index) || (GetTabCnt() < index)) return true; // Move to a tab (indexed 1 to 9). IAccessible *pi_access = NULL; HWND hwnd = GetChromeBrowserWnd(&pi_access); if (pi_access && hwnd) { // Activate main window and operate key Ctrl+digit. ActivateWnd(pi_access, hwnd); ClickKey(hwnd, VK_CONTROL, WORD('0'+index)); CHK_RELEASE(pi_access); // Set focused tab index. active_tab_index_ = index; // Return tab object. if (tab) { return GetTab(active_tab_index_, tab); } } return false; } bool CBrowserImpl::GoToNextTab(CTabImpl** tab) { IAccessible *pi_access = NULL; HWND hwnd = GetChromeBrowserWnd(&pi_access); if (pi_access && hwnd) { // Activate main window and operate key Ctrl+Tab. ActivateWnd(pi_access, hwnd); ClickKey(hwnd, VK_CONTROL, VK_TAB); CHK_RELEASE(pi_access); // Set focused tab index. if (active_tab_index_ == GetTabCnt()) { active_tab_index_ = 1; } else { active_tab_index_ = active_tab_index_ + 1; } // Return tab object. if (tab) { return GetTab(active_tab_index_, tab); } } return false; } bool CBrowserImpl::GoToPrevTab(CTabImpl** tab) { IAccessible *pi_access = NULL; HWND hwnd = GetChromeBrowserWnd(&pi_access); if (pi_access && hwnd) { // Activate main window and operate key Ctrl+Tab. ActivateWnd(pi_access, hwnd); ClickKey(hwnd, VK_SHIFT, VK_CONTROL, VK_TAB); CHK_RELEASE(pi_access); // Set focused tab index. if (active_tab_index_ == 1) { active_tab_index_ = GetTabCnt(); } else { active_tab_index_ = active_tab_index_ - 1; } // Return tab object. if (tab) { return GetTab(active_tab_index_, tab); } } return false; } bool CBrowserImpl::WaitForChromeToBeVisible(const INT64 interval, const INT64 timeout, bool* visible) { IAccessible *pi_access = NULL; INT64 time_elapsed = 0; *visible = false; // Check and wait. while (timeout >= time_elapsed) { GetTabStripWnd(&pi_access); if (pi_access) { *visible = true; CHK_RELEASE(pi_access); return true; } Sleep(DWORD(interval)); time_elapsed = time_elapsed + interval; } return false; } bool CBrowserImpl::WaitForTabCountToChange(const INT64 interval, const INT64 timeout, bool* changed) { // TODO: Add your implementation code here return true; } bool CBrowserImpl::WaitForTabToBecomeActive(const INT64 index, const INT64 interval, const INT64 timeout, bool* activated) { // TODO: Add your implementation code here return true; } bool CBrowserImpl::ApplyAccelerator(VARIANT keys) { // Input should be -array of enum or strings // or -IDispatch (jscript array object). if ((keys.vt != (VT_ARRAY|VT_BSTR)) && // Array of string values. (keys.vt != (VT_ARRAY|VT_I4)) && // Array of enum values. (!(keys.vt & VT_DISPATCH)) ) { // Object. return false; } // Array to store keys in a single combination. Currently, valid keyboard // -input combination can constitute of at the most 3 keys. KEYBD_KEYS key_value[3]; // Initialize key count. int key_cnt = 0; // Get variant array from object. IDispatch *disp = NULL; // Not array of string values or integers. if ((keys.vt != (VT_ARRAY|VT_BSTR)) && (keys.vt != (VT_ARRAY|VT_I4)) ) { // Retrive IDispatch. if (keys.vt & VT_BYREF) disp = *(keys.ppdispVal); else disp = keys.pdispVal; // Get array length. DISPPARAMS params; FillMemory(¶ms, sizeof(DISPPARAMS), 0); VARIANT res; DISPID id; LPOLESTR ln = L"length"; if (S_OK != disp->GetIDsOfNames(IID_NULL, &ln, 1, LOCALE_USER_DEFAULT, &id)) { return false; } if (S_OK != disp->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &res, NULL, NULL)) { return false; } VARIANT len; VariantInit(&len); VariantChangeType(&len, &res, 0, VT_I4); if (len.lVal > 3) return false; key_cnt = len.lVal; // Add elements to safe array. for (int i = 0; i < len.lVal; i++) { // Fetch element. wchar_t wstr[5]; memset(wstr, 0, 5*sizeof(wchar_t)); wsprintf(wstr, L"%d", i); LPOLESTR olestr = wstr; if (S_OK != disp->GetIDsOfNames(IID_NULL, &olestr, 1, LOCALE_USER_DEFAULT, &id)) { return false; } if (S_OK != disp->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &res, NULL, NULL)) { return false; } VARIANT value; VariantInit(&value); VariantChangeType(&value, &res, 0, VT_BSTR); // Translate and add key to array. key_value[i] = GetKeybdKeysVal(value.bstrVal); VariantClear(&value); } VariantClear(&len); } else { // Directly fetch array. SAFEARRAY *key_safe = NULL; key_safe = V_ARRAY(&keys); // Operate on Variant Array. HRESULT hr = S_OK; LONG cElements, lLBound, lUBound; // Array is not 1-dimentional. if (SafeArrayGetDim(key_safe) != 1) return false; // Get array bounds. hr = SafeArrayGetLBound(key_safe, 1, &lLBound); if (S_OK !=hr) return false; hr = SafeArrayGetUBound(key_safe, 1, &lUBound); if (S_OK !=hr) return false; // Key combination can be of maximum 3 keys. cElements = lUBound-lLBound+1; if (cElements > 3) return false; key_cnt = cElements; // Read the data in array. if (keys.vt == (VT_ARRAY|VT_I4)) { KEYBD_KEYS *read_keys; hr = SafeArrayAccessData(key_safe, reinterpret_cast(&read_keys)); if (S_OK !=hr) return false; for (int i = 0; i < cElements; i++) { key_value[i] = read_keys[i]; } } else if (keys.vt == (VT_ARRAY|VT_BSTR)) { BSTR *key_str_value; hr = SafeArrayAccessData(key_safe, reinterpret_cast(&key_str_value)); if (S_OK !=hr) return false; // Translate and add key to array. for (int i = 0; i < cElements; i++) { key_value[i] = GetKeybdKeysVal(key_str_value[i]); } } } // Focus on main window and operate keys. IAccessible *pi_access = NULL; HWND hwnd = GetChromeBrowserWnd(&pi_access); if (pi_access || hwnd) ActivateWnd(pi_access, hwnd); if (1 == key_cnt) ClickKey(hwnd, key_value[0]); else if (2 == key_cnt) ClickKey(hwnd, key_value[0], key_value[1]); else if (3 == key_cnt) ClickKey(hwnd, key_value[0], key_value[1], key_value[2]); CHK_RELEASE(pi_access); return true; } void CBrowserImpl::UpdateTabCollection(void) { // Get tab count and browser title. INT64 tab_cnt = GetTabCnt(); BSTR browser_title; GetBrowserTitle(&browser_title); // Check tab-collection size and no. of existing tabs, // work accordingly. // First time creation if (0 == tab_collection_.size()) { EraseTabCollection(); for (int i = 0; i < tab_cnt; i++) { tab_collection_[i]->index_ = i + 1; tab_collection_[i]->title_ = SysAllocString(GetTabName(tab_collection_[i]->index_)); if (browser_title == tab_collection_[i]->title_) { active_tab_index_ = tab_collection_[i]->index_; } } } // TODO: If tabs are swapped. // Add implementation here. } void CBrowserImpl::EraseTabCollection(void) { std::vector::iterator tab_iterator; for (tab_iterator = tab_collection_.begin(); tab_iterator != tab_collection_.end(); tab_iterator++) { // Relese memory used for data. CHK_DELETE(*tab_iterator); } tab_collection_.clear(); } void CBrowserImpl::CloseTabFromCollection(INT64 index) { std::vector ::size_type collection_size = tab_collection_.size(); // Validate tab index. if ((index < MIN_TAB_INDEX_DIGIT) || (static_cast(index) > collection_size) ) return; // Index starts from 1. tab_collection_.erase(tab_collection_.begin() + static_cast(index) - 1); // Now update tab collection data. collection_size = tab_collection_.size(); // Check if tab deleted is last tab. if (index-1 == collection_size) { // Change active tab index, only if tab deleted is last tab. active_tab_index_ = index - 1; } else { for (std::vector ::size_type i = static_cast(index) - 1; i < collection_size; i++) { tab_collection_[i]->index_--; } } }