blob: 3a5353812a484e0eb18b6d2a7640238f230df488 [file] [log] [blame]
/*
* 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 = (LPARAM) 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, (void*)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 = (LPARAM) 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;
}