Path: blob/master/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp
40957 views
/*1* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jaccesswalker.h"26#include "AccessInfo.h"2728HWND ourHwnd;29HWND topLevelWindow;30int depth = -1;31FILE *logfile;32HMENU popupMenu;3334char theJaccesswalkerClassName[] = "JaccesswalkerWin";35char theAccessInfoClassName[] = "AccessInfoWin";3637HWND theJaccesswalkerWindow;38HWND theTreeControlWindow;39HINSTANCE theInstance;40Jaccesswalker *theJaccesswalker;41AccessibleNode *theSelectedNode;42AccessibleNode *thePopupNode;43AccessibleContext theSelectedAccessibleContext;44HWND hwndTV; // handle of tree-view control4546int APIENTRY WinMain(HINSTANCE hInstance,47HINSTANCE hPrevInstance,48LPSTR lpCmdLine,49int nCmdShow)50{5152if (logfile == null) {53logfile = fopen(JACCESSWALKER_LOG, "w"); // overwrite existing log file54logString(logfile, "Starting jaccesswalker.exe %s\n", getTimeAndDate());55}5657theInstance = hInstance;5859// start Jaccesswalker60theJaccesswalker = new Jaccesswalker(nCmdShow);6162return 0;63}6465Jaccesswalker::Jaccesswalker(int nCmdShow) {6667HWND hwnd;68static char szAppName[] = "jaccesswalker";69static char szMenuName[] = "JACCESSWALKERMENU";70MSG msg;71WNDCLASSEX wc;7273// jaccesswalker window74wc.cbSize = sizeof(wc);75wc.style = CS_HREDRAW | CS_VREDRAW;76wc.lpfnWndProc = WinProc;77wc.cbClsExtra = 0;78wc.cbWndExtra = 0;79wc.hInstance = theInstance;80wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);81wc.hCursor = LoadCursor(NULL, IDI_APPLICATION);82wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);83wc.lpszMenuName = szMenuName;84wc.lpszClassName = szAppName;85wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);8687RegisterClassEx(&wc);8889// AccessInfo Window90wc.cbSize = sizeof(WNDCLASSEX);9192wc.hInstance = theInstance;93wc.lpszClassName = theAccessInfoClassName;94wc.lpfnWndProc = (WNDPROC)AccessInfoWindowProc;95wc.style = 0;9697wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);98wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);99wc.hCursor = LoadCursor(NULL, IDC_ARROW);100101wc.lpszMenuName = "";102wc.cbClsExtra = 0;103wc.cbWndExtra = 0;104105wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);106107RegisterClassEx(&wc);108109// create the jaccesswalker window110hwnd = CreateWindow(szAppName,111szAppName,112WS_OVERLAPPEDWINDOW,113CW_USEDEFAULT,114CW_USEDEFAULT,115CW_USEDEFAULT,116CW_USEDEFAULT,117NULL,118NULL,119theInstance,120NULL);121122ourHwnd = hwnd;123124/* Initialize the common controls. */125INITCOMMONCONTROLSEX cc;126cc.dwSize = sizeof(INITCOMMONCONTROLSEX);127cc.dwICC = ICC_TREEVIEW_CLASSES;128InitCommonControlsEx(&cc);129130ShowWindow(hwnd, nCmdShow);131132UpdateWindow(hwnd);133134BOOL result = initializeAccessBridge();135if (result != FALSE) {136while (GetMessage(&msg, NULL, 0, 0)) {137TranslateMessage(&msg);138DispatchMessage(&msg);139}140shutdownAccessBridge();141}142}143144/*145* the jaccesswalker window proc146*/147LRESULT CALLBACK WinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {148149int command;150short width, height;151152switch(iMsg) {153154case WM_CREATE:155// create the accessibility tree view156theTreeControlWindow = CreateATreeView(hwnd);157158// load the popup menu159popupMenu = LoadMenu(theInstance, "PopupMenu");160popupMenu = GetSubMenu(popupMenu, 0);161break;162163case WM_CLOSE:164EndDialog(hwnd, TRUE);165PostQuitMessage (0);166break;167168case WM_SIZE:169width = LOWORD(lParam);170height = HIWORD(lParam);171SetWindowPos(theTreeControlWindow, NULL, 0, 0, width, height, 0);172return FALSE; // let windows finish handling this173174case WM_COMMAND:175command = LOWORD(wParam);176switch(command) {177178case cExitMenuItem:179EndDialog(hwnd, TRUE);180PostQuitMessage (0);181break;182183case cRefreshTreeItem:184// update the accessibility tree185theJaccesswalker->buildAccessibilityTree();186break;187188case cAPIMenuItem:189// open a new window with the Accessibility API in it for the190// selected element in the tree191if (theSelectedNode != (AccessibleNode *) 0) {192theSelectedNode->displayAPIWindow();193}194break;195196case cAPIPopupItem:197// open a new window with the Accessibility API in it for the198// element in the tree adjacent to the popup menu199if (thePopupNode != (AccessibleNode *) 0) {200thePopupNode->displayAPIWindow();201}202break;203204}205break;206207case WM_NOTIFY: // receive tree messages208209NMTREEVIEW *nmptr = (LPNMTREEVIEW) lParam;210switch (nmptr->hdr.code) {211212case TVN_SELCHANGED:213// get the selected tree node214theSelectedNode = (AccessibleNode *) nmptr->itemNew.lParam;215break;216217case NM_RCLICK:218219// display a popup menu over the tree node220POINT p;221GetCursorPos(&p);222TrackPopupMenu(popupMenu, 0, p.x, p.y, 0, hwnd, NULL);223224// get the tree node under the popup menu225TVHITTESTINFO hitinfo;226ScreenToClient(theTreeControlWindow, &p);227hitinfo.pt = p;228HTREEITEM node = TreeView_HitTest(theTreeControlWindow, &hitinfo);229230if (node != null) {231TVITEMEX tvItem;232tvItem.hItem = node;233if (TreeView_GetItem(hwndTV, &tvItem) == TRUE) {234thePopupNode = (AccessibleNode *)tvItem.lParam;235}236}237break;238}239}240return DefWindowProc(hwnd, iMsg, wParam, lParam);241}242243/*244* Accessibility information window proc245*/246LRESULT CALLBACK AccessInfoWindowProc(HWND hWnd, UINT message,247UINT wParam, LONG lParam) {248short width, height;249HWND dlgItem;250251switch (message) {252case WM_CREATE:253RECT rcClient; // dimensions of client area254HWND hwndEdit; // handle of tree-view control255256// Get the dimensions of the parent window's client area,257// and create the edit control.258GetClientRect(hWnd, &rcClient);259hwndEdit = CreateWindow("Edit",260"",261WS_VISIBLE | WS_TABSTOP | WS_CHILD |262ES_MULTILINE | ES_AUTOVSCROLL |263ES_READONLY | WS_VSCROLL,2640, 0, rcClient.right, rcClient.bottom,265hWnd,266(HMENU) cAccessInfoText,267theInstance,268NULL);269break;270271case WM_CLOSE:272DestroyWindow(hWnd);273break;274275case WM_SIZE:276width = LOWORD(lParam);277height = HIWORD(lParam);278dlgItem = GetDlgItem(hWnd, cAccessInfoText);279SetWindowPos(dlgItem, NULL, 0, 0, width, height, 0);280return FALSE; // let windows finish handling this281break;282283default:284return DefWindowProc(hWnd, message, wParam, lParam);285}286287return 0;288}289290/**291* Build a tree (and the treeview control) of all accessible Java components292*293*/294void Jaccesswalker::buildAccessibilityTree() {295TreeView_DeleteAllItems (theTreeControlWindow);296// have MS-Windows call EnumWndProc() with all of the top-level windows297EnumWindows((WNDENUMPROC) EnumWndProc, NULL);298}299300/**301* Create (and display) the accessible component nodes of a parent AccessibleContext302*303*/304BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam) {305if (IsJavaWindow(hwnd)) {306long vmID;307AccessibleContext ac;308if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac) == TRUE) {309theJaccesswalker->addComponentNodes(vmID, ac, (AccessibleNode *) NULL,310hwnd, TVI_ROOT, theTreeControlWindow);311}312topLevelWindow = hwnd;313} else {314char szClass [MAX_PATH] = {0};315::GetClassNameA(hwnd, szClass, sizeof(szClass) - 1);316if ( ( 0 == ::strcmp(szClass, "IEFrame") )317|| ( 0 == ::strcmp(szClass, "MozillaUIWindowClass") ) ) {318EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL);319}320}321return TRUE;322}323324/*325Detects whether or not the specified Java window is one from which no useable326information can be obtained.327328This function tests for various scenarios I have seen in Java applets where the329Java applet had no meaningful accessible information. It does not detect all330scenarios, just the most common ones.331*/332BOOL IsInaccessibleJavaWindow(const HWND hwnd)333{334BOOL ret_val ( FALSE );335{336BOOL bT ( FALSE );337long vmIdWindow ( 0 );338AccessibleContext acWindow ( 0 );339bT = GetAccessibleContextFromHWND(hwnd, &vmIdWindow, &acWindow);340if ( ( bT ) && ( 0 != vmIdWindow ) && ( 0 != acWindow ) ) {341AccessibleContextInfo infoWindow = {0};342bT = GetAccessibleContextInfo(vmIdWindow, acWindow, &infoWindow);343if ( ( bT )344&& ( 0 == infoWindow.name [0] )345&& ( 0 == infoWindow.description [0] )346&& ( 0 == ::wcscmp(infoWindow.role_en_US, L"frame") ) ) {347if ( 0 == infoWindow.childrenCount ) {348ret_val = TRUE;349} else if ( 1 == infoWindow.childrenCount ) {350AccessibleContext acChild ( 0 );351acChild =352GetAccessibleChildFromContext(vmIdWindow, acWindow, 0);353if ( NULL != acChild ) {354AccessibleContextInfo infoChild = {0};355bT = GetAccessibleContextInfo( vmIdWindow, acChild,356&infoChild );357if ( ( bT )358&& ( 0 == infoChild.name [0] )359&& ( 0 == infoChild.description [0] )360&& ( 0 == ::wcscmp(infoChild.role_en_US, L"panel") )361&& ( 1 == infoChild.childrenCount ) ) {362AccessibleContext acChild1 ( 0 );363acChild1 = GetAccessibleChildFromContext( vmIdWindow,364acChild, 0);365if ( NULL != acChild1 ) {366AccessibleContextInfo infoChild1 = {0};367bT = GetAccessibleContextInfo( vmIdWindow,368acChild1, &infoChild1 );369if ( ( bT )370&& ( 0 == infoChild1.name [0] )371&& ( 0 == infoChild1.description [0] )372&& ( 0 == ::wcscmp(infoChild1.role_en_US, L"frame") )373&& ( 0 == infoChild1.childrenCount ) ) {374ret_val = TRUE;375} else if ( ( bT )376&& ( 0 == infoChild1.name [0] )377&& ( 0 == infoChild1.description [0] )378&& ( 0 == ::wcscmp( infoChild1.role_en_US,379L"panel") )380&& ( 1 == infoChild1.childrenCount ) ) {381AccessibleContext acChild2 ( 0 );382acChild2 = GetAccessibleChildFromContext(383vmIdWindow, acChild1, 0 );384if ( NULL != acChild2 ) {385AccessibleContextInfo infoChild2 = {0};386bT = GetAccessibleContextInfo(387vmIdWindow, acChild2, &infoChild2 );388if ( ( bT )389&& ( 0 == infoChild2.name [0] )390&& ( 0 == infoChild2.description [0] )391&& ( 0 == ::wcscmp( infoChild2.role_en_US,392L"frame") )393&& ( 0 == infoChild2.childrenCount ) ) {394ret_val = TRUE;395}396}397}398}399} else if ( ( bT )400&& ( 0 == infoChild.name [0] )401&& ( 0 == infoChild.description [0] )402&& ( 0 == ::wcscmp( infoChild.role_en_US,403L"canvas") )404&& ( 0 == infoChild.childrenCount ) ) {405ret_val = TRUE;406}407}408}409} else if ( ( bT )410&& ( 0 == infoWindow.name [0] )411&& ( 0 == infoWindow.description [0] )412&& ( 0 == ::wcscmp(infoWindow.role_en_US, L"panel") ) ) {413if ( 1 == infoWindow.childrenCount ) {414AccessibleContext acChild ( 0 );415acChild = GetAccessibleChildFromContext( vmIdWindow,416acWindow, 0 );417if ( NULL != acChild ) {418AccessibleContextInfo infoChild = {0};419bT = GetAccessibleContextInfo( vmIdWindow,420acChild, &infoChild );421if ( ( bT )422&& ( 0 == infoChild.name [0] )423&& ( 0 == infoChild.description [0] )424&& ( 0 == ::wcscmp(infoChild.role_en_US, L"frame") )425&& ( 0 == infoChild.childrenCount ) ) {426ret_val = TRUE;427} else if ( ( bT )428&& ( 0 == infoChild.name [0] )429&& ( 0 == infoChild.description [0] )430&& ( 0 == ::wcscmp( infoChild.role_en_US,431L"panel") )432&& ( 1 == infoChild.childrenCount ) ) {433AccessibleContext acChild1 ( 0 );434acChild1 = GetAccessibleChildFromContext( vmIdWindow,435acChild, 0);436if ( NULL != acChild1 ) {437AccessibleContextInfo infoChild1 = {0};438bT = GetAccessibleContextInfo( vmIdWindow,439acChild1,440&infoChild1 );441if ( ( bT )442&& ( 0 == infoChild1.name [0] )443&& ( 0 == infoChild1.description [0] )444&& ( 0 == ::wcscmp( infoChild1.role_en_US,445L"frame") )446&& ( 0 == infoChild1.childrenCount ) ) {447ret_val = TRUE;448}449}450}451}452}453}454} else if ( FALSE == bT ) {455ret_val = TRUE;456}457}458return ret_val;459}460461BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)462{463if ( ( IsJavaWindow(hwnd) )464&& ( FALSE == IsInaccessibleJavaWindow(hwnd) ) ) {465long vmID ( 0 );466AccessibleContext ac ( 0 );467if ( TRUE == GetAccessibleContextFromHWND(hwnd, &vmID, &ac) ) {468theJaccesswalker->addComponentNodes(469vmID, ac, (AccessibleNode *) NULL,470hwnd, TVI_ROOT, theTreeControlWindow);471}472topLevelWindow = hwnd;473} else {474EnumChildWindows(hwnd, (WNDENUMPROC) EnumChildProc, NULL);475}476return TRUE;477}478479// CreateATreeView - creates a tree-view control.480// Returns the handle of the new control if successful or NULL481// otherwise.482// hwndParent - handle of the control's parent window483HWND CreateATreeView(HWND hwndParent) {484RECT rcClient; // dimensions of client area485486// Get the dimensions of the parent window's client area, and create487// the tree-view control.488GetClientRect(hwndParent, &rcClient);489hwndTV = CreateWindow(WC_TREEVIEW,490"",491WS_VISIBLE | WS_TABSTOP | WS_CHILD |492TVS_HASLINES | TVS_HASBUTTONS |493TVS_LINESATROOT,4940, 0, rcClient.right, rcClient.bottom,495hwndParent,496(HMENU) cTreeControl,497theInstance,498NULL);499500return hwndTV;501}502503/**504* Create (and display) the accessible component nodes of a parent AccessibleContext505*506*/507void Jaccesswalker::addComponentNodes(long vmID, AccessibleContext context,508AccessibleNode *parent, HWND hwnd,509HTREEITEM treeNodeParent, HWND treeWnd) {510511AccessibleNode *newNode = new AccessibleNode( vmID, context, parent, hwnd,512treeNodeParent );513514AccessibleContextInfo info;515if (GetAccessibleContextInfo(vmID, context, &info) != FALSE) {516char s[LINE_BUFSIZE];517518wsprintf(s, "%ls", info.name);519newNode->setAccessibleName(s);520wsprintf(s, "%ls", info.role);521newNode->setAccessibleRole(s);522523wsprintf(s, "%ls [%ls]", info.name, info.role);524525TVITEM tvi;526tvi.mask = TVIF_PARAM | TVIF_TEXT;527tvi.pszText = (char *) s; // Accessible name and role528tvi.cchTextMax = (int)strlen(s);529tvi.lParam = (LPARAM) newNode; // Accessibility information530531TVINSERTSTRUCT tvis;532tvis.hParent = treeNodeParent;533tvis.hInsertAfter = TVI_LAST;534tvis.item = tvi;535536HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis);537538for (int i = 0; i < info.childrenCount; i++) {539addComponentNodes(vmID, GetAccessibleChildFromContext(vmID, context, i),540newNode, hwnd, treeNodeItem, treeWnd);541}542} else {543char s[LINE_BUFSIZE];544sprintf( s,545"ERROR calling GetAccessibleContextInfo; vmID = %X, context = %p",546vmID, (void*)context );547548TVITEM tvi;549tvi.mask = TVIF_PARAM | TVIF_TEXT; // text and lParam are only valid parts550tvi.pszText = (char *) s;551tvi.cchTextMax = (int)strlen(s);552tvi.lParam = (LPARAM) newNode;553554TVINSERTSTRUCT tvis;555tvis.hParent = treeNodeParent;556tvis.hInsertAfter = TVI_LAST; // make tree in order given557tvis.item = tvi;558559HTREEITEM treeNodeItem = TreeView_InsertItem(treeWnd, &tvis);560}561}562563// -----------------------------564565/**566* Create an AccessibleNode567*568*/569AccessibleNode::AccessibleNode(long JavaVMID, AccessibleContext context,570AccessibleNode *parent, HWND hwnd,571HTREEITEM parentTreeNodeItem) {572vmID = JavaVMID;573ac = context;574parentNode = parent;575baseHWND = hwnd;576treeNodeParent = parentTreeNodeItem;577578// setting accessibleName and accessibleRole not done here,579// in order to minimize calls to the AccessBridge580// (since such a call is needed to enumerate children)581}582583/**584* Destroy an AccessibleNode585*586*/587AccessibleNode::~AccessibleNode() {588ReleaseJavaObject(vmID, ac);589}590591/**592* Set the accessibleName string593*594*/595void AccessibleNode::setAccessibleName(char *name) {596strncpy(accessibleName, name, MAX_STRING_SIZE);597}598599/**600* Set the accessibleRole string601*602*/603void AccessibleNode::setAccessibleRole(char *role) {604strncpy(accessibleRole, role, SHORT_STRING_SIZE);605}606607608609610611612613/**614* Create an API window to show off the info for this AccessibleContext615*/616BOOL AccessibleNode::displayAPIWindow() {617618HWND apiWindow = CreateWindow(theAccessInfoClassName,619"Java Accessibility API view",620WS_OVERLAPPEDWINDOW,621CW_USEDEFAULT,622CW_USEDEFAULT,623600,624750,625HWND_DESKTOP,626NULL,627theInstance,628(void *) NULL);629630if (!apiWindow) {631printError("cannot create API window");632return FALSE;633}634635char buffer[HUGE_BUFSIZE];636buffer[0] = '\0';637getAccessibleInfo(vmID, ac, buffer, sizeof(buffer));638displayAndLog(apiWindow, cAccessInfoText, logfile, buffer);639640ShowWindow(apiWindow, SW_SHOWNORMAL);641UpdateWindow(apiWindow);642643return TRUE;644}645646647648649650