| /* |
| * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "jaccesswalker.h" |
| #include "AccessInfo.h" |
| |
| HWND ourHwnd; |
| HWND topLevelWindow; |
| int depth = -1; |
| FILE *logfile; |
| HMENU popupMenu; |
| |
| char theJaccesswalkerClassName[] = "JaccesswalkerWin"; |
| char theAccessInfoClassName[] = "AccessInfoWin"; |
| |
| HWND theJaccesswalkerWindow; |
| HWND theTreeControlWindow; |
| HINSTANCE theInstance; |
| Jaccesswalker *theJaccesswalker; |
| AccessibleNode *theSelectedNode; |
| AccessibleNode *thePopupNode; |
| AccessibleContext theSelectedAccessibleContext; |
| HWND hwndTV; // handle of tree-view control |
| |
| int APIENTRY WinMain(HINSTANCE hInstance, |
| HINSTANCE hPrevInstance, |
| LPSTR lpCmdLine, |
| int nCmdShow) |
| { |
| |
| if (logfile == null) { |
| logfile = fopen(JACCESSWALKER_LOG, "w"); // overwrite existing log file |
| logString(logfile, "Starting jaccesswalker.exe %s\n", getTimeAndDate()); |
| } |
| |
| theInstance = hInstance; |
| |
| // start Jaccesswalker |
| theJaccesswalker = new Jaccesswalker(nCmdShow); |
| |
| return 0; |
| } |
| |
| Jaccesswalker::Jaccesswalker(int nCmdShow) { |
| |
| HWND hwnd; |
| static char szAppName[] = "jaccesswalker"; |
| static char szMenuName[] = "JACCESSWALKERMENU"; |
| MSG msg; |
| WNDCLASSEX wc; |
| |
| // jaccesswalker window |
| wc.cbSize = sizeof(wc); |
| wc.style = CS_HREDRAW | CS_VREDRAW; |
| wc.lpfnWndProc = WinProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = theInstance; |
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); |
| wc.hCursor = LoadCursor(NULL, IDI_APPLICATION); |
| wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = szMenuName; |
| wc.lpszClassName = szAppName; |
| wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); |
| |
| RegisterClassEx(&wc); |
| |
| // AccessInfo Window |
| wc.cbSize = sizeof(WNDCLASSEX); |
| |
| wc.hInstance = theInstance; |
| wc.lpszClassName = theAccessInfoClassName; |
| wc.lpfnWndProc = (WNDPROC)AccessInfoWindowProc; |
| wc.style = 0; |
| |
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); |
| wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); |
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); |
| |
| wc.lpszMenuName = ""; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); |
| |
| RegisterClassEx(&wc); |
| |
| // create the jaccesswalker window |
| hwnd = CreateWindow(szAppName, |
| szAppName, |
| WS_OVERLAPPEDWINDOW, |
| CW_USEDEFAULT, |
| CW_USEDEFAULT, |
| CW_USEDEFAULT, |
| CW_USEDEFAULT, |
| NULL, |
| NULL, |
| theInstance, |
| NULL); |
| |
| ourHwnd = hwnd; |
| |
| /* Initialize the common controls. */ |
| INITCOMMONCONTROLSEX cc; |
| cc.dwSize = sizeof(INITCOMMONCONTROLSEX); |
| cc.dwICC = ICC_TREEVIEW_CLASSES; |
| InitCommonControlsEx(&cc); |
| |
| ShowWindow(hwnd, nCmdShow); |
| |
| UpdateWindow(hwnd); |
| |
| BOOL result = initializeAccessBridge(); |
| if (result != FALSE) { |
| while (GetMessage(&msg, NULL, 0, 0)) { |
| TranslateMessage(&msg); |
| DispatchMessage(&msg); |
| } |
| shutdownAccessBridge(); |
| } |
| } |
| |
| /* |
| * the jaccesswalker window proc |
| */ |
| LRESULT CALLBACK WinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { |
| |
| int command; |
| short width, height; |
| |
| switch(iMsg) { |
| |
| case WM_CREATE: |
| // create the accessibility tree view |
| theTreeControlWindow = CreateATreeView(hwnd); |
| |
| // load the popup menu |
| popupMenu = LoadMenu(theInstance, "PopupMenu"); |
| popupMenu = GetSubMenu(popupMenu, 0); |
| break; |
| |
| case WM_CLOSE: |
| EndDialog(hwnd, TRUE); |
| PostQuitMessage (0); |
| break; |
| |
| case WM_SIZE: |
| width = LOWORD(lParam); |
| height = HIWORD(lParam); |
| SetWindowPos(theTreeControlWindow, NULL, 0, 0, width, height, 0); |
| return FALSE; // let windows finish handling this |
| |
| case WM_COMMAND: |
| command = LOWORD(wParam); |
| switch(command) { |
| |
| case cExitMenuItem: |
| EndDialog(hwnd, TRUE); |
| PostQuitMessage (0); |
| break; |
| |
| case cRefreshTreeItem: |
| // update the accessibility tree |
| theJaccesswalker->buildAccessibilityTree(); |
| break; |
| |
| case cAPIMenuItem: |
| // open a new window with the Accessibility API in it for the |
| // selected element in the tree |
| if (theSelectedNode != (AccessibleNode *) 0) { |
| theSelectedNode->displayAPIWindow(); |
| } |
| break; |
| |
| case cAPIPopupItem: |
| // open a new window with the Accessibility API in it for the |
| // element in the tree adjacent to the popup menu |
| if (thePopupNode != (AccessibleNode *) 0) { |
| thePopupNode->displayAPIWindow(); |
| } |
| break; |
| |
| } |
| break; |
| |
| case WM_NOTIFY: // receive tree messages |
| |
| NMTREEVIEW *nmptr = (LPNMTREEVIEW) lParam; |
| switch (nmptr->hdr.code) { |
| |
| case TVN_SELCHANGED: |
| // get the selected tree node |
| theSelectedNode = (AccessibleNode *) nmptr->itemNew.lParam; |
| break; |
| |
| case NM_RCLICK: |
| |
| // display a popup menu over the tree node |
| POINT p; |
| GetCursorPos(&p); |
| TrackPopupMenu(popupMenu, 0, p.x, p.y, 0, hwnd, NULL); |
| |
| // get the tree node under the popup menu |
| TVHITTESTINFO hitinfo; |
| ScreenToClient(theTreeControlWindow, &p); |
| hitinfo.pt = p; |
| HTREEITEM node = TreeView_HitTest(theTreeControlWindow, &hitinfo); |
| |
| if (node != null) { |
| TVITEMEX tvItem; |
| tvItem.hItem = node; |
| if (TreeView_GetItem(hwndTV, &tvItem) == TRUE) { |
| thePopupNode = (AccessibleNode *)tvItem.lParam; |
| } |
| } |
| break; |
| } |
| } |
| return DefWindowProc(hwnd, iMsg, wParam, lParam); |
| } |
| |
| /* |
| * Accessibility information window proc |
| */ |
| LRESULT CALLBACK AccessInfoWindowProc(HWND hWnd, UINT message, |
| UINT wParam, LONG lParam) { |
| short width, height; |
| HWND dlgItem; |
| |
| switch (message) { |
| case WM_CREATE: |
| RECT rcClient; // dimensions of client area |
| HWND hwndEdit; // handle of tree-view control |
| |
| // Get the dimensions of the parent window's client area, |
| // and create the edit control. |
| GetClientRect(hWnd, &rcClient); |
| hwndEdit = CreateWindow("Edit", |
| "", |
| WS_VISIBLE | WS_TABSTOP | WS_CHILD | |
| ES_MULTILINE | ES_AUTOVSCROLL | |
| ES_READONLY | WS_VSCROLL, |
| 0, 0, rcClient.right, rcClient.bottom, |
| hWnd, |
| (HMENU) cAccessInfoText, |
| theInstance, |
| NULL); |
| break; |
| |
| case WM_CLOSE: |
| DestroyWindow(hWnd); |
| break; |
| |
| case WM_SIZE: |
| width = LOWORD(lParam); |
| height = HIWORD(lParam); |
| dlgItem = GetDlgItem(hWnd, cAccessInfoText); |
| SetWindowPos(dlgItem, NULL, 0, 0, width, height, 0); |
| return FALSE; // let windows finish handling this |
| break; |
| |
| default: |
| return DefWindowProc(hWnd, message, wParam, lParam); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Build a tree (and the treeview control) of all accessible Java components |
| * |
| */ |
| void Jaccesswalker::buildAccessibilityTree() { |
| TreeView_DeleteAllItems (theTreeControlWindow); |
| // have MS-Windows call EnumWndProc() with all of the top-level windows |
| EnumWindows((WNDENUMPROC) EnumWndProc, NULL); |
| } |
| |
| /** |
| * Create (and display) the accessible component nodes of a parent AccessibleContext |
| * |
| */ |
| BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam) { |
| if (IsJavaWindow(hwnd)) { |
| long vmID; |
| AccessibleContext ac; |
| if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac) == TRUE) { |
| theJaccesswalker->addComponentNodes(vmID, ac, (AccessibleNode *) NULL, |
| hwnd, TVI_ROOT, theTreeControlWindow); |
| } |
| topLevelWindow = hwnd; |
| } else { |
| char szClass [MAX_PATH] = {0}; |
| ::GetClassNameA(hwnd, szClass, sizeof(szClass) - 1); |
| if ( ( 0 == ::strcmp(szClass, "IEFrame") ) |
| || ( 0 == ::strcmp(szClass, "MozillaUIWindowClass") ) ) { |
| EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); |
| } |
| } |
| return TRUE; |
| } |
| |
| /* |
| Detects whether or not the specified Java window is one from which no useable |
| information can be obtained. |
| |
| This function tests for various scenarios I have seen in Java applets where the |
| Java applet had no meaningful accessible information. It does not detect all |
| scenarios, just the most common ones. |
| */ |
| BOOL IsInaccessibleJavaWindow(const HWND hwnd) |
| { |
| BOOL ret_val ( FALSE ); |
| { |
| BOOL bT ( FALSE ); |
| long vmIdWindow ( 0 ); |
| AccessibleContext acWindow ( 0 ); |
| bT = GetAccessibleContextFromHWND(hwnd, &vmIdWindow, &acWindow); |
| if ( ( bT ) && ( 0 != vmIdWindow ) && ( 0 != acWindow ) ) { |
| AccessibleContextInfo infoWindow = {0}; |
| bT = GetAccessibleContextInfo(vmIdWindow, acWindow, &infoWindow); |
| if ( ( bT ) |
| && ( 0 == infoWindow.name [0] ) |
| && ( 0 == infoWindow.description [0] ) |
| && ( 0 == ::wcscmp(infoWindow.role_en_US, L"frame") ) ) { |
| if ( 0 == infoWindow.childrenCount ) { |
| ret_val = TRUE; |
| } else if ( 1 == infoWindow.childrenCount ) { |
| AccessibleContext acChild ( 0 ); |
| acChild = |
| GetAccessibleChildFromContext(vmIdWindow, acWindow, 0); |
| if ( NULL != acChild ) { |
| AccessibleContextInfo infoChild = {0}; |
| bT = GetAccessibleContextInfo( vmIdWindow, acChild, |
| &infoChild ); |
| if ( ( bT ) |
| && ( 0 == infoChild.name [0] ) |
| && ( 0 == infoChild.description [0] ) |
| && ( 0 == ::wcscmp(infoChild.role_en_US, L"panel") ) |
| && ( 1 == infoChild.childrenCount ) ) { |
| AccessibleContext acChild1 ( 0 ); |
| acChild1 = GetAccessibleChildFromContext( vmIdWindow, |
| acChild, 0); |
| if ( NULL != acChild1 ) { |
| AccessibleContextInfo infoChild1 = {0}; |
| bT = GetAccessibleContextInfo( vmIdWindow, |
| acChild1, &infoChild1 ); |
| if ( ( bT ) |
| && ( 0 == infoChild1.name [0] ) |
| && ( 0 == infoChild1.description [0] ) |
| && ( 0 == ::wcscmp(infoChild1.role_en_US, L"frame") ) |
| && ( 0 == infoChild1.childrenCount ) ) { |
| ret_val = TRUE; |
| } else if ( ( bT ) |
| && ( 0 == infoChild1.name [0] ) |
| && ( 0 == infoChild1.description [0] ) |
| && ( 0 == ::wcscmp( infoChild1.role_en_US, |
| L"panel") ) |
| && ( 1 == infoChild1.childrenCount ) ) { |
| AccessibleContext acChild2 ( 0 ); |
| acChild2 = GetAccessibleChildFromContext( |
| vmIdWindow, acChild1, 0 ); |
| if ( NULL != acChild2 ) { |
| AccessibleContextInfo infoChild2 = {0}; |
| bT = GetAccessibleContextInfo( |
| vmIdWindow, acChild2, &infoChild2 ); |
| if ( ( bT ) |
| && ( 0 == infoChild2.name [0] ) |
| && ( 0 == infoChild2.description [0] ) |
| && ( 0 == ::wcscmp( infoChild2.role_en_US, |
| L"frame") ) |
| && ( 0 == infoChild2.childrenCount ) ) { |
| ret_val = TRUE; |
| } |
| } |
| } |
| } |
| } else if ( ( bT ) |
| && ( 0 == infoChild.name [0] ) |
| && ( 0 == infoChild.description [0] ) |
| && ( 0 == ::wcscmp( infoChild.role_en_US, |
| L"canvas") ) |
| && ( 0 == infoChild.childrenCount ) ) { |
| ret_val = TRUE; |
| } |
| } |
| } |
| } else if ( ( bT ) |
| && ( 0 == infoWindow.name [0] ) |
| && ( 0 == infoWindow.description [0] ) |
| && ( 0 == ::wcscmp(infoWindow.role_en_US, L"panel") ) ) { |
| if ( 1 == infoWindow.childrenCount ) { |
| AccessibleContext acChild ( 0 ); |
| acChild = GetAccessibleChildFromContext( vmIdWindow, |
| acWindow, 0 ); |
| if ( NULL != acChild ) { |
| AccessibleContextInfo infoChild = {0}; |
| bT = GetAccessibleContextInfo( vmIdWindow, |
| acChild, &infoChild ); |
| if ( ( bT ) |
| && ( 0 == infoChild.name [0] ) |
| && ( 0 == infoChild.description [0] ) |
| && ( 0 == ::wcscmp(infoChild.role_en_US, L"frame") ) |
| && ( 0 == infoChild.childrenCount ) ) { |
| ret_val = TRUE; |
| } else if ( ( bT ) |
| && ( 0 == infoChild.name [0] ) |
| && ( 0 == infoChild.description [0] ) |
| && ( 0 == ::wcscmp( infoChild.role_en_US, |
| L"panel") ) |
| && ( 1 == infoChild.childrenCount ) ) { |
| AccessibleContext acChild1 ( 0 ); |
| acChild1 = GetAccessibleChildFromContext( vmIdWindow, |
| acChild, 0); |
| if ( NULL != acChild1 ) { |
| AccessibleContextInfo infoChild1 = {0}; |
| bT = GetAccessibleContextInfo( vmIdWindow, |
| acChild1, |
| &infoChild1 ); |
| if ( ( bT ) |
| && ( 0 == infoChild1.name [0] ) |
| && ( 0 == infoChild1.description [0] ) |
| && ( 0 == ::wcscmp( infoChild1.role_en_US, |
| L"frame") ) |
| && ( 0 == infoChild1.childrenCount ) ) { |
| ret_val = TRUE; |
| } |
| } |
| } |
| } |
| } |
| } |
| } else if ( FALSE == bT ) { |
| ret_val = TRUE; |
| } |
| } |
| return ret_val; |
| } |
| |
| BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) |
| { |
| if ( ( IsJavaWindow(hwnd) ) |
| && ( FALSE == IsInaccessibleJavaWindow(hwnd) ) ) { |
| long vmID ( 0 ); |
| AccessibleContext ac ( 0 ); |
| if ( TRUE == GetAccessibleContextFromHWND(hwnd, &vmID, &ac) ) { |
| theJaccesswalker->addComponentNodes( |
| vmID, ac, (AccessibleNode *) NULL, |
| hwnd, TVI_ROOT, theTreeControlWindow); |
| } |
| topLevelWindow = hwnd; |
| } else { |
| EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL); |
| } |
| return TRUE; |
| } |
| |
| // CreateATreeView - creates a tree-view control. |
| // Returns the handle of the new control if successful or NULL |
| // otherwise. |
| // hwndParent - handle of the control's parent window |
| HWND CreateATreeView(HWND hwndParent) { |
| RECT rcClient; // dimensions of client area |
| |
| // Get the dimensions of the parent window's client area, and create |
| // the tree-view control. |
| GetClientRect(hwndParent, &rcClient); |
| hwndTV = CreateWindow(WC_TREEVIEW, |
| "", |
| WS_VISIBLE | WS_TABSTOP | WS_CHILD | |
| TVS_HASLINES | TVS_HASBUTTONS | |
| TVS_LINESATROOT, |
| 0, 0, rcClient.right, rcClient.bottom, |
| hwndParent, |
| (HMENU) cTreeControl, |
| theInstance, |
| NULL); |
| |
| return hwndTV; |
| } |
| |
| /** |
| * Create (and display) the accessible component nodes of a parent AccessibleContext |
| * |
| */ |
| void Jaccesswalker::addComponentNodes(long vmID, AccessibleContext context, |
| AccessibleNode *parent, HWND hwnd, |
| HTREEITEM treeNodeParent, HWND treeWnd) { |
| |
| AccessibleNode *newNode = new AccessibleNode( vmID, context, parent, hwnd, |
| treeNodeParent ); |
| |
| AccessibleContextInfo info; |
| if (GetAccessibleContextInfo(vmID, context, &info) != FALSE) { |
| char s[LINE_BUFSIZE]; |
| |
| wsprintf(s, "%ls", info.name); |
| newNode->setAccessibleName(s); |
| wsprintf(s, "%ls", info.role); |
| newNode->setAccessibleRole(s); |
| |
| wsprintf(s, "%ls [%ls]", info.name, info.role); |
| |
| TVITEM tvi; |
| tvi.mask = TVIF_PARAM | TVIF_TEXT; |
| tvi.pszText = (char *) s; // Accessible name and role |
| tvi.cchTextMax = (int)strlen(s); |
| tvi.lParam = (long) newNode; // Accessibility information |
| |
| TVINSERTSTRUCT tvis; |
| tvis.hParent = treeNodeParent; |
| tvis.hInsertAfter = TVI_LAST; |
| tvis.item = tvi; |
| |
| HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); |
| |
| for (int i = 0; i < info.childrenCount; i++) { |
| addComponentNodes(vmID, GetAccessibleChildFromContext(vmID, context, i), |
| newNode, hwnd, treeNodeItem, treeWnd); |
| } |
| } else { |
| char s[LINE_BUFSIZE]; |
| sprintf( s, |
| "ERROR calling GetAccessibleContextInfo; vmID = %X, context = %p", |
| vmID, context ); |
| |
| TVITEM tvi; |
| tvi.mask = TVIF_PARAM | TVIF_TEXT; // text and lParam are only valid parts |
| tvi.pszText = (char *) s; |
| tvi.cchTextMax = (int)strlen(s); |
| tvi.lParam = (long) newNode; |
| |
| TVINSERTSTRUCT tvis; |
| tvis.hParent = treeNodeParent; |
| tvis.hInsertAfter = TVI_LAST; // make tree in order given |
| tvis.item = tvi; |
| |
| HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis); |
| } |
| } |
| |
| // ----------------------------- |
| |
| /** |
| * Create an AccessibleNode |
| * |
| */ |
| AccessibleNode::AccessibleNode(long JavaVMID, AccessibleContext context, |
| AccessibleNode *parent, HWND hwnd, |
| HTREEITEM parentTreeNodeItem) { |
| vmID = JavaVMID; |
| ac = context; |
| parentNode = parent; |
| baseHWND = hwnd; |
| treeNodeParent = parentTreeNodeItem; |
| |
| // setting accessibleName and accessibleRole not done here, |
| // in order to minimize calls to the AccessBridge |
| // (since such a call is needed to enumerate children) |
| } |
| |
| /** |
| * Destroy an AccessibleNode |
| * |
| */ |
| AccessibleNode::~AccessibleNode() { |
| ReleaseJavaObject(vmID, ac); |
| } |
| |
| /** |
| * Set the accessibleName string |
| * |
| */ |
| void AccessibleNode::setAccessibleName(char *name) { |
| strncpy(accessibleName, name, MAX_STRING_SIZE); |
| } |
| |
| /** |
| * Set the accessibleRole string |
| * |
| */ |
| void AccessibleNode::setAccessibleRole(char *role) { |
| strncpy(accessibleRole, role, SHORT_STRING_SIZE); |
| } |
| |
| |
| |
| |
| |
| |
| |
| /** |
| * Create an API window to show off the info for this AccessibleContext |
| */ |
| BOOL AccessibleNode::displayAPIWindow() { |
| |
| HWND apiWindow = CreateWindow(theAccessInfoClassName, |
| "Java Accessibility API view", |
| WS_OVERLAPPEDWINDOW, |
| CW_USEDEFAULT, |
| CW_USEDEFAULT, |
| 600, |
| 750, |
| HWND_DESKTOP, |
| NULL, |
| theInstance, |
| (void *) NULL); |
| |
| if (!apiWindow) { |
| printError("cannot create API window"); |
| return FALSE; |
| } |
| |
| char buffer[HUGE_BUFSIZE]; |
| buffer[0] = '\0'; |
| getAccessibleInfo(vmID, ac, buffer, sizeof(buffer)); |
| displayAndLog(apiWindow, cAccessInfoText, logfile, buffer); |
| |
| ShowWindow(apiWindow, SW_SHOWNORMAL); |
| UpdateWindow(apiWindow); |
| |
| return TRUE; |
| } |
| |
| |
| |