Bug 591874 - windows screen readers are broken due to post-130078 changes in the native widget structure, r=marcoz, davidb, a=blocking

This commit is contained in:
Alexander Surkov 2010-09-16 20:23:17 -07:00
Родитель a554f1f1f7
Коммит 67cc22e9ac
12 изменённых файлов: 267 добавлений и 122 удалений

Просмотреть файл

@ -93,7 +93,7 @@ nsIAtom *nsDocAccessible::gLastFocusedFrameType = nsnull;
nsDocAccessible::
nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
nsIWeakReference *aShell) :
nsHyperTextAccessibleWrap(aRootContent, aShell), mWnd(nsnull),
nsHyperTextAccessibleWrap(aRootContent, aShell),
mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE)
{
// XXX aaronl should we use an algorithm for the initial cache size?
@ -103,17 +103,6 @@ nsDocAccessible::
if (!mDocument)
return;
// Initialize mWnd
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
nsIViewManager* vm = shell->GetViewManager();
if (vm) {
nsCOMPtr<nsIWidget> widget;
vm->GetRootWidget(getter_AddRefs(widget));
if (widget) {
mWnd = widget->GetNativeData(NS_NATIVE_WINDOW);
}
}
// nsAccDocManager creates document accessible when scrollable frame is
// available already, it should be safe time to add scroll listener.
AddScrollListener();
@ -473,7 +462,8 @@ NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAStr
NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
{
*aWindow = mWnd;
NS_ENSURE_ARG_POINTER(aWindow);
*aWindow = GetNativeWindow();
return NS_OK;
}
@ -1335,6 +1325,20 @@ nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent)
////////////////////////////////////////////////////////////////////////////////
// Public members
void*
nsDocAccessible::GetNativeWindow() const
{
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
nsIViewManager* vm = shell->GetViewManager();
if (vm) {
nsCOMPtr<nsIWidget> widget;
vm->GetRootWidget(getter_AddRefs(widget));
if (widget)
return widget->GetNativeData(NS_NATIVE_WINDOW);
}
return nsnull;
}
nsAccessible*
nsDocAccessible::GetCachedAccessibleInSubtree(void* aUniqueID)
{

Просмотреть файл

@ -141,6 +141,11 @@ public:
*/
void MarkAsLoaded() { mIsLoaded = PR_TRUE; }
/**
* Return a native window handler or pointer depending on platform.
*/
virtual void* GetNativeWindow() const;
/**
* Return the parent document.
*/
@ -363,7 +368,6 @@ protected:
*/
nsAccessibleHashtable mAccessibleCache;
void *mWnd;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsITimer> mScrollWatchTimer;
PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events

Просмотреть файл

@ -594,9 +594,13 @@ GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
nsAccessibleWrap *accWrap = static_cast<nsAccessibleWrap*>(mGeckoAccessible);
// Get a pointer to the native window (NSWindow) we reside in.
NSWindow *nativeWindow = nil;
accWrap->GetNativeWindow ((void**)&nativeWindow);
nsDocAccessible* docAcc = accWrap->GetDocAccessible();
if (docAcc)
nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
NSAssert1(nativeWindow, @"Could not get native window for %@", self);
return nativeWindow;

Просмотреть файл

@ -71,10 +71,7 @@ class nsAccessibleWrap : public nsAccessible
// should be instantied with. used on runtime to determine the
// right type for this accessible's associated native object.
virtual objc_class* GetNativeType ();
// returns a pointer to the native window for this accessible tree.
void GetNativeWindow (void **aOutNativeWindow);
virtual void Shutdown ();
virtual void InvalidateChildren();

Просмотреть файл

@ -86,17 +86,6 @@ nsAccessibleWrap::GetNativeInterface (void **aOutInterface)
return NS_ERROR_FAILURE;
}
// get the native NSWindow we reside in.
void
nsAccessibleWrap::GetNativeWindow (void **aOutNativeWindow)
{
*aOutNativeWindow = nsnull;
nsDocAccessible *docAcc = GetDocAccessible();
if (docAcc)
docAcc->GetWindowHandle (aOutNativeWindow);
}
// overridden in subclasses to create the right kind of object. by default we create a generic
// 'mozAccessible' node.
objc_class*

Просмотреть файл

@ -46,6 +46,7 @@
#include "nsApplicationAccessibleWrap.h"
#include "nsCoreUtils.h"
#include "nsRootAccessible.h"
#include "nsWinUtils.h"
#include "nsAttrName.h"
#include "nsIDocument.h"
@ -63,6 +64,7 @@
HINSTANCE nsAccessNodeWrap::gmAccLib = nsnull;
HINSTANCE nsAccessNodeWrap::gmUserLib = nsnull;
LPFNACCESSIBLEOBJECTFROMWINDOW nsAccessNodeWrap::gmAccessibleObjectFromWindow = nsnull;
LPFNLRESULTFROMOBJECT nsAccessNodeWrap::gmLresultFromObject = NULL;
LPFNNOTIFYWINEVENT nsAccessNodeWrap::gmNotifyWinEvent = nsnull;
LPFNGETGUITHREADINFO nsAccessNodeWrap::gmGetGUIThreadInfo = nsnull;
@ -569,7 +571,14 @@ void nsAccessNodeWrap::InitAccessibility()
}
DoATSpecificProcessing();
// Register window class that'll be used for document accessibles associated
// with tabs.
if (nsWinUtils::IsWindowEmulationEnabled()) {
nsWinUtils::RegisterNativeWindow(kClassNameTabContent);
sHWNDCache.Init(4);
}
nsAccessNode::InitXPAccessibility();
}
@ -578,6 +587,11 @@ void nsAccessNodeWrap::ShutdownAccessibility()
NS_IF_RELEASE(gTextEvent);
::DestroyCaret();
// Unregister window call that's used for document accessibles associated
// with tabs.
if (nsWinUtils::IsWindowEmulationEnabled())
::UnregisterClassW(kClassNameTabContent, GetModuleHandle(NULL));
nsAccessNode::ShutdownXPAccessibility();
}
@ -624,7 +638,7 @@ GetHRESULT(nsresult aResult)
PRBool nsAccessNodeWrap::IsOnlyMsaaCompatibleJawsPresent()
{
HMODULE jhookhandle = ::GetModuleHandleW(L"jhook");
HMODULE jhookhandle = ::GetModuleHandleW(kJAWSModuleHandle);
if (!jhookhandle)
return PR_FALSE; // No JAWS, or some other screen reader, use IA2
@ -655,10 +669,10 @@ PRBool nsAccessNodeWrap::IsOnlyMsaaCompatibleJawsPresent()
void nsAccessNodeWrap::TurnOffNewTabSwitchingForJawsAndWE()
{
HMODULE srHandle = ::GetModuleHandleW(L"jhook");
HMODULE srHandle = ::GetModuleHandleW(kJAWSModuleHandle);
if (!srHandle) {
// No JAWS, try Window-Eyes
srHandle = ::GetModuleHandleW(L"gwm32inc");
srHandle = ::GetModuleHandleW(kWEModuleHandle);
if (!srHandle) {
// no screen reader we're interested in. Bail out.
return;
@ -694,3 +708,49 @@ void nsAccessNodeWrap::DoATSpecificProcessing()
TurnOffNewTabSwitchingForJawsAndWE();
}
nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible> nsAccessNodeWrap::sHWNDCache;
LRESULT CALLBACK
nsAccessNodeWrap::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_GETOBJECT:
{
if (lParam == OBJID_CLIENT) {
nsDocAccessible* document = sHWNDCache.GetWeak(static_cast<void*>(hWnd));
if (document) {
IAccessible* msaaAccessible = NULL;
document->GetNativeInterface((void**)&msaaAccessible); // does an addref
if (msaaAccessible) {
LRESULT result = LresultFromObject(IID_IAccessible, wParam,
msaaAccessible); // does an addref
msaaAccessible->Release(); // release extra addref
return result;
}
}
}
return 0;
}
}
return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}
STDMETHODIMP_(LRESULT)
nsAccessNodeWrap::LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc)
{
// open the dll dynamically
if (!gmAccLib)
gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
if (gmAccLib) {
if (!gmLresultFromObject)
gmLresultFromObject = (LPFNLRESULTFROMOBJECT)GetProcAddress(gmAccLib,"LresultFromObject");
if (gmLresultFromObject)
return gmLresultFromObject(riid, wParam, pAcc);
}
return 0;
}

Просмотреть файл

@ -68,6 +68,8 @@
#include "nsICrashReporter.h"
#endif
#include "nsRefPtrHashtable.h"
typedef LRESULT (STDAPICALLTYPE *LPFNNOTIFYWINEVENT)(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
typedef LRESULT (STDAPICALLTYPE *LPFNGETGUITHREADINFO)(DWORD idThread, GUITHREADINFO* pgui);
@ -154,6 +156,7 @@ public: // construction, destruction
static HINSTANCE gmAccLib;
static HINSTANCE gmUserLib;
static LPFNACCESSIBLEOBJECTFROMWINDOW gmAccessibleObjectFromWindow;
static LPFNLRESULTFROMOBJECT gmLresultFromObject;
static LPFNNOTIFYWINEVENT gmNotifyWinEvent;
static LPFNGETGUITHREADINFO gmGetGUIThreadInfo;
@ -165,6 +168,13 @@ public: // construction, destruction
static void DoATSpecificProcessing();
static STDMETHODIMP_(LRESULT) LresultFromObject(REFIID riid, WPARAM wParam, LPUNKNOWN pAcc);
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg,
WPARAM WParam, LPARAM lParam);
static nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible> sHWNDCache;
protected:
/**

Просмотреть файл

@ -42,6 +42,7 @@
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsRelUtils.h"
#include "nsWinUtils.h"
#include "nsIAccessibleDocument.h"
#include "nsIAccessibleEvent.h"
@ -193,58 +194,23 @@ STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *pp
{
__try {
*ppdispParent = NULL;
if (!mWeakShell)
return E_FAIL; // We've been shut down
nsIFrame *frame = GetFrame();
HWND hwnd = 0;
if (frame) {
nsIView *view = frame->GetViewExternal();
if (view) {
// This code is essentially our implementation of WindowFromAccessibleObject,
// because MSAA iterates get_accParent() until it sees an object of ROLE_WINDOW
// to know where the window for a given accessible is. We must expose the native
// window accessible that MSAA creates for us. This must be done for the document
// object as well as any layout that creates its own window (e.g. via overflow: scroll)
nsIWidget *widget = view->GetWidget();
if (widget) {
hwnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
NS_ASSERTION(hwnd, "No window handle for window");
if (IsDefunct())
return E_FAIL;
nsIViewManager* viewManager = view->GetViewManager();
if (!viewManager)
return E_UNEXPECTED;
nsIView *rootView;
viewManager->GetRootView(rootView);
if (rootView == view) {
// If the client accessible (OBJID_CLIENT) has a window but its window
// was created by an outer window then we want the native accessible
// for that outer window. If the accessible was created for outer
// window (if the outer window has inner windows then they share the
// same client accessible with it) then return native accessible for
// the outer window.
HWND parenthwnd = ::GetParent(hwnd);
if (parenthwnd)
hwnd = parenthwnd;
NS_ASSERTION(hwnd, "No window handle for window");
}
nsRefPtr<nsDocAccessible> doc(do_QueryObject(this));
if (doc) {
// Return window system accessible object for root document and tab document
// accessibles.
if (!doc->ParentDocument() ||
nsWinUtils::IsWindowEmulationEnabled() &&
nsWinUtils::IsTabDocument(doc->GetDocumentNode())) {
HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
IID_IAccessible,
(void**)ppdispParent))) {
return S_OK;
}
else {
// If a frame is a scrollable frame, then it has one window for the client area,
// not an extra parent window for just the scrollbars
nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
if (scrollFrame) {
hwnd = (HWND)scrollFrame->GetScrolledFrame()->GetNearestWidget()->GetNativeData(NS_NATIVE_WINDOW);
NS_ASSERTION(hwnd, "No window handle for window");
}
}
}
if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
(void**)ppdispParent))) {
return S_OK;
}
}
@ -1668,45 +1634,11 @@ PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
HWND
nsAccessibleWrap::GetHWNDFor(nsAccessible *aAccessible)
{
HWND hWnd = 0;
if (!aAccessible)
return hWnd;
return 0;
nsIFrame *frame = aAccessible->GetFrame();
if (frame) {
nsIWidget *window = frame->GetNearestWidget();
PRBool isVisible;
window->IsVisible(isVisible);
if (isVisible) {
// Short explanation:
// If HWND for frame is inside a hidden window, fire the event on the
// containing document's visible window.
//
// Long explanation:
// This is really just to fix combo boxes with JAWS. Window-Eyes already
// worked with combo boxes because they use the value change event in
// the closed combo box case. JAWS will only pay attention to the focus
// events on the list items. The JAWS developers haven't fixed that, so
// we'll use the focus events to make JAWS work. However, JAWS is
// ignoring events on a hidden window. So, in order to fix the bug where
// JAWS doesn't echo the current option as it changes in a closed
// combo box, we need to use an ensure that we never fire an event with
// an HWND for a hidden window.
hWnd = (HWND)frame->GetNearestWidget()->GetNativeData(NS_NATIVE_WINDOW);
}
}
if (!hWnd) {
void* handle = nsnull;
nsDocAccessible *accessibleDoc = aAccessible->GetDocAccessible();
if (!accessibleDoc)
return 0;
accessibleDoc->GetWindowHandle(&handle);
hWnd = (HWND)handle;
}
return hWnd;
nsDocAccessible* document = aAccessible->GetDocAccessible();
return document ? static_cast<HWND>(document->GetNativeWindow()) : 0;
}
HRESULT

Просмотреть файл

@ -39,6 +39,9 @@
#include "nsDocAccessibleWrap.h"
#include "ISimpleDOMDocument_i.c"
#include "nsIAccessibilityService.h"
#include "nsRootAccessible.h"
#include "nsWinUtils.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeNode.h"
#include "nsIFrame.h"
@ -60,7 +63,7 @@
nsDocAccessibleWrap::
nsDocAccessibleWrap(nsIDocument *aDocument, nsIContent *aRootContent,
nsIWeakReference *aShell) :
nsDocAccessible(aDocument, aRootContent, aShell)
nsDocAccessible(aDocument, aRootContent, aShell), mHWND(NULL)
{
}
@ -245,3 +248,53 @@ STDMETHODIMP nsDocAccessibleWrap::get_accValue(
return get_URL(pszValue);
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessNode
PRBool
nsDocAccessibleWrap::Init()
{
if (nsWinUtils::IsWindowEmulationEnabled()) {
// Create window for tab document.
if (nsWinUtils::IsTabDocument(mDocument)) {
nsRefPtr<nsRootAccessible> root = GetRootAccessible();
mHWND = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
static_cast<HWND>(root->GetNativeWindow()));
nsAccessibleWrap::sHWNDCache.Put(mHWND, this);
} else {
nsDocAccessible* parentDocument = ParentDocument();
if (parentDocument)
mHWND = parentDocument->GetNativeWindow();
}
}
return nsDocAccessible::Init();
}
void
nsDocAccessibleWrap::Shutdown()
{
if (nsWinUtils::IsWindowEmulationEnabled()) {
// Destroy window created for root document.
if (nsWinUtils::IsTabDocument(mDocument)) {
nsAccessibleWrap::sHWNDCache.Remove(mHWND);
::DestroyWindow(static_cast<HWND>(mHWND));
}
mHWND = nsnull;
}
nsDocAccessible::Shutdown();
}
////////////////////////////////////////////////////////////////////////////////
// nsDocAccessible
void*
nsDocAccessibleWrap::GetNativeWindow() const
{
return mHWND ? mHWND : nsDocAccessible::GetNativeWindow();
}

Просмотреть файл

@ -89,8 +89,18 @@ public:
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ BSTR __RPC_FAR *pszValue);
// nsAccessNode
virtual PRBool Init();
virtual void Shutdown();
// nsAccessibleWrap
virtual nsAccessible *GetXPAccessibleFor(const VARIANT& varChild);
// nsDocAccessible
virtual void* GetNativeWindow() const;
protected:
void* mHWND;
};
#endif

Просмотреть файл

@ -43,6 +43,7 @@
#include "nsAccessibleWrap.h"
#include "nsIWinAccessNode.h"
#include "nsArrayUtils.h"
#include "nsIDocShellTreeItem.h"
HRESULT
nsWinUtils::ConvertToIA2Array(nsIArray *aGeckoArray, IUnknown ***aIA2Array,
@ -96,3 +97,56 @@ nsWinUtils::ConvertToIA2Array(nsIArray *aGeckoArray, IUnknown ***aIA2Array,
*aIA2ArrayLen = length;
return S_OK;
}
void
nsWinUtils::RegisterNativeWindow(LPCWSTR aWindowClass)
{
WNDCLASSW wc;
wc.style = CS_GLOBALCLASS;
wc.lpfnWndProc = nsAccessNodeWrap::WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = aWindowClass;
::RegisterClassW(&wc);
}
HWND
nsWinUtils::CreateNativeWindow(LPCWSTR aWindowClass, HWND aParentWnd)
{
return ::CreateWindowW(aWindowClass,
L"NetscapeDispatchWnd",
WS_CHILD | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0,
aParentWnd,
NULL,
GetModuleHandle(NULL),
NULL);
}
bool
nsWinUtils::IsWindowEmulationEnabled()
{
return ::GetModuleHandleW(kJAWSModuleHandle) ||
::GetModuleHandleW(kWEModuleHandle);
}
bool
nsWinUtils::IsTabDocument(nsIDocument* aDocumentNode)
{
nsCOMPtr<nsISupports> container = aDocumentNode->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
treeItem->GetParent(getter_AddRefs(parentTreeItem));
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
return parentTreeItem == rootTreeItem;
}

Просмотреть файл

@ -44,6 +44,14 @@
#include "Accessible2.h"
#include "nsIArray.h"
#include "nsIDocument.h"
const LPCWSTR kClassNameRoot = L"MozillaUIWindowClass";
const LPCWSTR kClassNameTabContent = L"MozillaContentWindowClass";
const LPCWSTR kJAWSModuleHandle = L"jhook";
const LPCWSTR kWEModuleHandle = L"gwm32inc";
const LPCWSTR kNVDAModuleHandle = L"VBufBackend_gecko_ia2";
class nsWinUtils
{
@ -54,6 +62,26 @@ public:
*/
static HRESULT ConvertToIA2Array(nsIArray *aCollection,
IUnknown ***aAccessibles, long *aCount);
/**
* Helper to register window class.
*/
static void RegisterNativeWindow(LPCWSTR aWindowClass);
/**
* Helper to create a window.
*/
static HWND CreateNativeWindow(LPCWSTR aWindowClass, HWND aParentWnd);
/**
* Return true if window emulation is enabled.
*/
static bool IsWindowEmulationEnabled();
/**
* Return true if the given document node is for tab document accessible.
*/
static bool IsTabDocument(nsIDocument* aDocumentNode);
};
#endif