mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 381888. Caret tracking broken with Windows screen readers [Cairo-related]. When a11y active, follow Gecko caret around with invisible system caret. r=surkov
This commit is contained in:
parent
1423e531e9
commit
cf34398740
@ -59,7 +59,6 @@ XPIDLSRCS = \
|
||||
nsIAccessibleRole.idl \
|
||||
nsIAccessibleStates.idl \
|
||||
nsPIAccessible.idl \
|
||||
nsIAccessibleCaret.idl \
|
||||
nsIAccessibleDocument.idl \
|
||||
nsPIAccessibleDocument.idl \
|
||||
nsIAccessibleProvider.idl \
|
||||
|
@ -1,77 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla browser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIDOMNode.idl"
|
||||
|
||||
[scriptable, uuid(ef372cc1-0fd4-4e71-ac32-64028e680f17)]
|
||||
interface nsIAccessibleCaret : nsISupports
|
||||
{
|
||||
/**
|
||||
* Listen to selection events on the focused control.
|
||||
* Only one control's selection events are listened to at a time, per top-level window.
|
||||
* This will remove the previous control's selection listener.
|
||||
* It will fail if aFocusedNode is a document node -- document selection must be listened
|
||||
* to via addDocSelectionListener().
|
||||
* @param aFocusedNode The node for the focused control
|
||||
*/
|
||||
void setControlSelectionListener(in nsIDOMNode aFocusedNode);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events for any control.
|
||||
* This does not have to be called explicitly in Shutdown() procedures,
|
||||
* because the implementation of nsIAccessibleCaret will guarantee that.
|
||||
*/
|
||||
void clearControlSelectionListener();
|
||||
|
||||
/**
|
||||
* Start listening to selection events for a given document
|
||||
* More than one document's selection events can be listened to
|
||||
* at the same time, by a given nsIAccessibleCaret.
|
||||
* @param aDocument Document to listen to selection events for.
|
||||
*/
|
||||
void addDocSelectionListener(in nsIDOMDocument aDocument);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events for a given document
|
||||
* If the document goes away, this method needs to be called for
|
||||
* that document by the owner of the nsIAccessibleCaret
|
||||
* @param aDocument Document to listen to selection events for.
|
||||
*/
|
||||
void removeDocSelectionListener(in nsIDOMDocument aDocument);
|
||||
};
|
@ -58,7 +58,7 @@ interface nsIDOMWindow;
|
||||
*
|
||||
* @status UNDER_REVIEW
|
||||
*/
|
||||
[scriptable, uuid(60f8b190-63c4-4919-ac92-a7a8212fc813)]
|
||||
[scriptable, uuid(ae5792a3-77ad-40d6-8450-d28d19b66889)]
|
||||
interface nsIAccessibleDocument : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -91,11 +91,6 @@ interface nsIAccessibleDocument : nsISupports
|
||||
*/
|
||||
readonly attribute nsIDOMWindow window;
|
||||
|
||||
/*
|
||||
* The accessible for the caret belonging to this window.
|
||||
*/
|
||||
readonly attribute nsIAccessible caretAccessible;
|
||||
|
||||
/**
|
||||
* The namespace for each ID that is handed back.
|
||||
*/
|
||||
|
@ -354,9 +354,9 @@ already_AddRefed<nsRootAccessible> nsAccessNode::GetRootAccessible()
|
||||
|
||||
// nsRootAccessible has a special QI
|
||||
// that let us get that concrete type directly.
|
||||
nsRootAccessible* foo;
|
||||
accDoc->QueryInterface(NS_GET_IID(nsRootAccessible), (void**)&foo); // addrefs
|
||||
return foo;
|
||||
nsRootAccessible* rootAccessible;
|
||||
accDoc->QueryInterface(NS_GET_IID(nsRootAccessible), (void**)&rootAccessible); // addrefs
|
||||
return rootAccessible;
|
||||
}
|
||||
|
||||
nsIFrame* nsAccessNode::GetFrame()
|
||||
|
@ -53,46 +53,47 @@
|
||||
#include "nsIViewManager.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsCaretAccessible, nsLeafAccessible, nsIAccessibleCaret, nsISelectionListener)
|
||||
NS_IMPL_ISUPPORTS1(nsCaretAccessible, nsISelectionListener)
|
||||
|
||||
nsCaretAccessible::nsCaretAccessible(nsIDOMNode* aDocumentNode, nsIWeakReference* aShell, nsRootAccessible *aRootAccessible):
|
||||
nsLeafAccessible(aDocumentNode, aShell), mCurrentControl(nsnull), mLastNodeWithCaret(nsnull),
|
||||
nsCaretAccessible::nsCaretAccessible( nsRootAccessible *aRootAccessible):
|
||||
mLastCaretOffset(-1), mRootAccessible(aRootAccessible)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::Shutdown()
|
||||
nsCaretAccessible::~nsCaretAccessible()
|
||||
{
|
||||
}
|
||||
|
||||
void nsCaretAccessible::Shutdown()
|
||||
{
|
||||
// The caret accessible isn't shut down until the nsRootAccessible owning it is shut down
|
||||
// Each nsDocAccessible, including the nsRootAccessible, is responsible for clearing the
|
||||
// doc selection listeners they registeed in this nsCaretAccessible
|
||||
ClearControlSelectionListener(); // Clear the selection listener for the currently focused control
|
||||
mLastNodeWithCaret = nsnull;
|
||||
mLastUsedSelection = nsnull;
|
||||
// doc selection listeners they registered in this nsCaretAccessible
|
||||
|
||||
return nsLeafAccessible::Shutdown();
|
||||
ClearControlSelectionListener(); // Clear the selection listener for the currently focused control
|
||||
mLastTextAccessible = nsnull;
|
||||
mLastUsedSelection = nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::ClearControlSelectionListener()
|
||||
nsresult nsCaretAccessible::ClearControlSelectionListener()
|
||||
{
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryReferent(mCurrentControlSelection));
|
||||
if (selPrivate) {
|
||||
mCurrentControlSelection = nsnull;
|
||||
mCurrentControl = nsnull;
|
||||
return selPrivate->RemoveSelectionListener(this);
|
||||
}
|
||||
return NS_OK;
|
||||
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
|
||||
|
||||
mCurrentControlSelection = nsnull;
|
||||
mCurrentControl = nsnull;
|
||||
return selPrivate->RemoveSelectionListener(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::SetControlSelectionListener(nsIDOMNode *aCurrentNode)
|
||||
nsresult nsCaretAccessible::SetControlSelectionListener(nsIDOMNode *aCurrentNode)
|
||||
{
|
||||
mCurrentControl = aCurrentNode;
|
||||
mLastNodeWithCaret = nsnull;
|
||||
mLastTextAccessible = nsnull;
|
||||
|
||||
// When focus moves such that the caret is part of a new frame selection
|
||||
// this removes the old selection listener and attaches a new one for the current focus
|
||||
nsCOMPtr<nsIPresShell> presShell =
|
||||
nsRootAccessible::GetPresShellFor(aCurrentNode);
|
||||
mRootAccessible->GetPresShellFor(aCurrentNode);
|
||||
if (!presShell)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@ -127,7 +128,7 @@ NS_IMETHODIMP nsCaretAccessible::SetControlSelectionListener(nsIDOMNode *aCurren
|
||||
return selPrivate->AddSelectionListener(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::AddDocSelectionListener(nsIDOMDocument *aDoc)
|
||||
nsresult nsCaretAccessible::AddDocSelectionListener(nsIDOMDocument *aDoc)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
@ -142,7 +143,7 @@ NS_IMETHODIMP nsCaretAccessible::AddDocSelectionListener(nsIDOMDocument *aDoc)
|
||||
return selPrivate->AddSelectionListener(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::RemoveDocSelectionListener(nsIDOMDocument *aDoc)
|
||||
nsresult nsCaretAccessible::RemoveDocSelectionListener(nsIDOMDocument *aDoc)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
@ -168,13 +169,13 @@ NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, ns
|
||||
|
||||
// Get first nnsIAccessibleText in parent chain and fire caret-move, selection-change event for it
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
nsIAccessibilityService *accService = GetAccService();
|
||||
nsIAccessibilityService *accService = mRootAccessible->GetAccService();
|
||||
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
|
||||
// Get accessible from selection's focus node or its parent
|
||||
nsCOMPtr<nsIDOMNode> focusNode;
|
||||
aSel->GetFocusNode(getter_AddRefs(focusNode));
|
||||
if (!focusNode) {
|
||||
mLastNodeWithCaret = nsnull;
|
||||
mLastTextAccessible = nsnull;
|
||||
return NS_OK; // No selection
|
||||
}
|
||||
nsCOMPtr<nsIDOMNode> nodeWithCaret = focusNode;
|
||||
@ -203,9 +204,10 @@ NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, ns
|
||||
NS_ENSURE_TRUE(textAcc, NS_ERROR_FAILURE);
|
||||
|
||||
PRInt32 caretOffset;
|
||||
textAcc->GetCaretOffset(&caretOffset);
|
||||
nsresult rv = textAcc->GetCaretOffset(&caretOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (nodeWithCaret == mLastNodeWithCaret && caretOffset == mLastCaretOffset) {
|
||||
if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) {
|
||||
PRInt32 selectionCount;
|
||||
textAcc->GetSelectionCount(&selectionCount); // Don't swallow similar events when selecting text
|
||||
if (!selectionCount) {
|
||||
@ -213,122 +215,76 @@ NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, ns
|
||||
}
|
||||
}
|
||||
mLastCaretOffset = caretOffset;
|
||||
mLastNodeWithCaret = nodeWithCaret;
|
||||
mLastTextAccessible = textAcc;
|
||||
|
||||
return mRootAccessible->FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED,
|
||||
focusNode, nsnull, PR_FALSE);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICaret>
|
||||
nsCaretAccessible::GetLastCaret(nsRect *aRect, PRBool *aIsVisible)
|
||||
nsRect
|
||||
nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget)
|
||||
{
|
||||
*aIsVisible = PR_FALSE;
|
||||
if (!mLastNodeWithCaret) {
|
||||
return nsnull;
|
||||
nsRect caretRect;
|
||||
NS_ENSURE_TRUE(aOutWidget, caretRect);
|
||||
*aOutWidget = nsnull;
|
||||
|
||||
if (!mLastTextAccessible) {
|
||||
return caretRect; // Return empty rect
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShellFor(mLastNodeWithCaret);
|
||||
NS_ENSURE_TRUE(presShell, nsnull);
|
||||
nsCOMPtr<nsIAccessNode> lastAccessNode(do_QueryInterface(mLastTextAccessible));
|
||||
NS_ENSURE_TRUE(lastAccessNode, caretRect);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> lastNodeWithCaret;
|
||||
lastAccessNode->GetDOMNode(getter_AddRefs(lastNodeWithCaret));
|
||||
NS_ENSURE_TRUE(lastNodeWithCaret, caretRect);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = mRootAccessible->GetPresShellFor(lastNodeWithCaret);
|
||||
NS_ENSURE_TRUE(presShell, caretRect);
|
||||
|
||||
nsICaret *caret;
|
||||
presShell->GetCaret(&caret);
|
||||
NS_ENSURE_TRUE(caret, nsnull);
|
||||
NS_ENSURE_TRUE(caret, caretRect);
|
||||
|
||||
nsRect caretRect;
|
||||
PRBool isCollapsed;
|
||||
nsIView *view;
|
||||
nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection));
|
||||
caret->GetCaretCoordinates(nsICaret::eTopLevelWindowCoordinates, caretSelection,
|
||||
aRect, &isCollapsed, nsnull);
|
||||
if (!aRect->IsEmpty()) {
|
||||
caret->GetCaretVisible(aIsVisible);
|
||||
caret->GetCaretCoordinates(nsICaret::eRenderingViewCoordinates, caretSelection,
|
||||
&caretRect, &isCollapsed, &view);
|
||||
if (!view || caretRect.IsEmpty()) {
|
||||
return nsRect(); // Return empty rect
|
||||
}
|
||||
return caret;
|
||||
}
|
||||
|
||||
/** Return the caret's bounds */
|
||||
NS_IMETHODIMP nsCaretAccessible::GetBounds(PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight)
|
||||
{
|
||||
*aX = *aY = *aWidth = *aHeight = 0;
|
||||
nsRect caretRect;
|
||||
PRBool isVisible;
|
||||
nsCOMPtr<nsICaret> caret = GetLastCaret(&caretRect, &isVisible);
|
||||
if (!caret || caretRect.IsEmpty()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
caret->GetCaretVisible(&isVisible);
|
||||
if (!isVisible) {
|
||||
return nsRect(); // Return empty rect
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShellFor(mLastNodeWithCaret);
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
||||
nsPoint offsetFromWidget;
|
||||
*aOutWidget = view->GetNearestWidget(&offsetFromWidget);
|
||||
NS_ENSURE_TRUE(*aOutWidget, nsRect());
|
||||
|
||||
nsPresContext *presContext = presShell->GetPresContext();
|
||||
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(presContext, nsRect());
|
||||
|
||||
nsIViewManager* viewManager = presShell->GetViewManager();
|
||||
NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
|
||||
|
||||
nsIView *view = nsnull;
|
||||
viewManager->GetRootView(view);
|
||||
NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
|
||||
|
||||
nsIWidget* widget = view->GetWidget();
|
||||
NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
|
||||
|
||||
caretRect.x = presContext->AppUnitsToDevPixels(caretRect.x);
|
||||
caretRect.y = presContext->AppUnitsToDevPixels(caretRect.y);
|
||||
caretRect.width = presContext->AppUnitsToDevPixels(caretRect.width);
|
||||
caretRect.x = presContext->AppUnitsToDevPixels(caretRect.x + offsetFromWidget.x);
|
||||
caretRect.y = presContext->AppUnitsToDevPixels(caretRect.y + offsetFromWidget.y);
|
||||
caretRect.width = presContext->AppUnitsToDevPixels(caretRect.width);
|
||||
caretRect.height = presContext->AppUnitsToDevPixels(caretRect.height);
|
||||
|
||||
nsRect caretScreenRect;
|
||||
widget->WidgetToScreen(caretRect, caretScreenRect);
|
||||
(*aOutWidget)->WidgetToScreen(caretRect, caretRect);
|
||||
|
||||
*aX = caretScreenRect.x;
|
||||
*aY = caretScreenRect.y;
|
||||
*aWidth = caretScreenRect.width;
|
||||
*aHeight = caretScreenRect.height;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::GetRole(PRUint32 *_retval)
|
||||
{
|
||||
*_retval = nsIAccessibleRole::ROLE_CARET;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCaretAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
|
||||
{
|
||||
*aState = 0;
|
||||
if (aExtraState)
|
||||
*aExtraState = 0;
|
||||
|
||||
NS_ENSURE_TRUE(mLastNodeWithCaret, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShellFor(mLastNodeWithCaret);
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsRect caretRect;
|
||||
PRBool isVisible;
|
||||
GetLastCaret(&caretRect, &isVisible);
|
||||
if (!isVisible) {
|
||||
*aState = nsIAccessibleStates::STATE_INVISIBLE;
|
||||
// Correct for character size, so that caret always matches the size of the character
|
||||
// This is important for font size transitions, and is necessary because the Gecko caret uses the
|
||||
// previous character's size as the user moves forward in the text by character.
|
||||
PRInt32 charX, charY, charWidth, charHeight;
|
||||
if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY,
|
||||
&charWidth, &charHeight,
|
||||
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
|
||||
caretRect.height -= charY - caretRect.y;
|
||||
caretRect.y = charY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::GetParent(nsIAccessible **aParent)
|
||||
{
|
||||
NS_ADDREF(*aParent = mRootAccessible);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP nsCaretAccessible::GetPreviousSibling(nsIAccessible **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCaretAccessible::GetNextSibling(nsIAccessible **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
return NS_OK;
|
||||
|
||||
return caretRect;
|
||||
}
|
||||
|
||||
|
@ -38,14 +38,15 @@
|
||||
#ifndef __nsCaretAccessible_h__
|
||||
#define __nsCaretAccessible_h__
|
||||
|
||||
#include "nsBaseWidgetAccessible.h"
|
||||
#include "nsIWeakReference.h"
|
||||
#include "nsIAccessibleText.h"
|
||||
#include "nsICaret.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIAccessibleCaret.h"
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
class nsRootAccessible;
|
||||
class nsIView;
|
||||
|
||||
/*
|
||||
* This special accessibility class is for the caret, which is really the currently focused selection.
|
||||
@ -54,10 +55,11 @@ class nsRootAccessible;
|
||||
*
|
||||
* The important selections are the one owned by each document, and the one in the currently focused control.
|
||||
*
|
||||
* The caret accesible does not exist within the normal accessible tree; it lives in a different world.
|
||||
* In MSAA, it is retrieved with via the WM_GETOBJECT message with lParam = OBJID_CARET,
|
||||
* The caret accessible is no longer an accessible object in its own right.
|
||||
* On Windows it is used to move an invisible system caret that shadows the Mozilla caret. Windows will
|
||||
* also automatically map this to the MSAA caret accessible object (via OBJID_CARET).
|
||||
* (as opposed to the root accessible tree for a window which is retrieved with OBJID_CLIENT)
|
||||
* In ATK, the caret accessible is never exposed as an accessible object, but is used to fire
|
||||
* For ATK and Iaccessible2, the caret accessible is used to fire
|
||||
* caret move and selection change events.
|
||||
*
|
||||
* The caret accessible is owned by the nsRootAccessible for the top level window that it's in.
|
||||
@ -68,36 +70,52 @@ class nsRootAccessible;
|
||||
* selection listener when the doc goes away via removeDocSelectionListener().
|
||||
*/
|
||||
|
||||
class nsCaretAccessible : public nsLeafAccessible, public nsIAccessibleCaret, public nsISelectionListener
|
||||
class nsCaretAccessible : public nsISelectionListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsCaretAccessible(nsIDOMNode* aDocumentNode, nsIWeakReference* aShell, nsRootAccessible *aRootAccessible);
|
||||
|
||||
/* ----- nsIAccessible ----- */
|
||||
NS_IMETHOD GetParent(nsIAccessible **_retval);
|
||||
NS_IMETHOD GetRole(PRUint32 *_retval);
|
||||
NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
|
||||
NS_IMETHOD GetNextSibling(nsIAccessible **_retval);
|
||||
NS_IMETHOD GetPreviousSibling(nsIAccessible **_retval);
|
||||
|
||||
/* ----- nsIAccessibleCaret ------ */
|
||||
NS_DECL_NSIACCESSIBLECARET
|
||||
nsCaretAccessible(nsRootAccessible *aRootAccessible);
|
||||
virtual ~nsCaretAccessible();
|
||||
void Shutdown();
|
||||
|
||||
/* ----- nsISelectionListener ---- */
|
||||
NS_DECL_NSISELECTIONLISTENER
|
||||
|
||||
/* ----- nsIAccessNode ----- */
|
||||
NS_IMETHOD Init()
|
||||
{
|
||||
#ifdef DEBUG_A11Y
|
||||
mIsInitialized = PR_TRUE;
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD Shutdown();
|
||||
/**
|
||||
* Listen to selection events on the focused control.
|
||||
* Only one control's selection events are listened to at a time, per top-level window.
|
||||
* This will remove the previous control's selection listener.
|
||||
* It will fail if aFocusedNode is a document node -- document selection must be listened
|
||||
* to via AddDocSelectionListener().
|
||||
* @param aFocusedNode The node for the focused control
|
||||
*/
|
||||
nsresult SetControlSelectionListener(nsIDOMNode *aCurrentNode);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events for any control.
|
||||
* This does not have to be called explicitly in Shutdown() procedures,
|
||||
* because the nsCaretAccessible implementation guarantees that.
|
||||
*/
|
||||
nsresult ClearControlSelectionListener();
|
||||
|
||||
/**
|
||||
* Start listening to selection events for a given document
|
||||
* More than one document's selection events can be listened to
|
||||
* at the same time, by a given nsCaretAccessible
|
||||
* @param aDocument Document to listen to selection events for.
|
||||
*/
|
||||
nsresult AddDocSelectionListener(nsIDOMDocument *aDoc);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events for a given document
|
||||
* If the document goes away, this method needs to be called for
|
||||
* that document by the owner of the caret
|
||||
* @param aDocument Document to listen to selection events for.
|
||||
*/
|
||||
nsresult RemoveDocSelectionListener(nsIDOMDocument *aDoc);
|
||||
|
||||
nsRect GetCaretRect(nsIWidget **aOutWidget);
|
||||
|
||||
private:
|
||||
// The currently focused control -- never a document.
|
||||
@ -111,9 +129,8 @@ private:
|
||||
// If it was on a control, then mLastUsedSelection == mCurrentControlSelection
|
||||
// Otherwise, it's for a document where the selection changed
|
||||
nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection
|
||||
nsCOMPtr<nsIDOMNode> mLastNodeWithCaret;
|
||||
nsCOMPtr<nsIAccessibleText> mLastTextAccessible;
|
||||
PRInt32 mLastCaretOffset;
|
||||
already_AddRefed<nsICaret> GetLastCaret(nsRect *aRect, PRBool *aIsVisible);
|
||||
|
||||
nsRootAccessible *mRootAccessible;
|
||||
};
|
||||
|
@ -168,10 +168,10 @@ NS_IMETHODIMP nsDocAccessible::GetRole(PRUint32 *aRole)
|
||||
if (docShellTreeItem) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
||||
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
||||
PRInt32 itemType;
|
||||
docShellTreeItem->GetItemType(&itemType);
|
||||
if (sameTypeRoot == docShellTreeItem) {
|
||||
// Root of content or chrome tree
|
||||
PRInt32 itemType;
|
||||
docShellTreeItem->GetItemType(&itemType);
|
||||
if (itemType == nsIDocShellTreeItem::typeChrome) {
|
||||
*aRole = nsIAccessibleRole::ROLE_CHROME_WINDOW;
|
||||
}
|
||||
@ -188,6 +188,9 @@ NS_IMETHODIMP nsDocAccessible::GetRole(PRUint32 *aRole)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
||||
*aRole = nsIAccessibleRole::ROLE_DOCUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -349,13 +352,6 @@ NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAStr
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::GetCaretAccessible(nsIAccessible **aCaretAccessible)
|
||||
{
|
||||
// We only have a caret accessible on the root document
|
||||
*aCaretAccessible = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
|
||||
{
|
||||
*aWindow = mWnd;
|
||||
@ -666,13 +662,13 @@ nsresult nsDocAccessible::AddEventListeners()
|
||||
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
||||
docShellTreeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
|
||||
if (rootTreeItem) {
|
||||
nsCOMPtr<nsIAccessibleDocument> rootAccDoc = GetDocAccessibleFor(rootTreeItem, PR_TRUE);
|
||||
nsCOMPtr<nsIAccessible> caretAccessible;
|
||||
rootAccDoc->GetCaretAccessible(getter_AddRefs(caretAccessible));
|
||||
nsCOMPtr<nsIAccessibleCaret> caretAccessibleIface(do_QueryInterface(caretAccessible));
|
||||
if (caretAccessibleIface) {
|
||||
GetDocAccessibleFor(rootTreeItem, PR_TRUE); // Ensure root accessible is created;
|
||||
nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
|
||||
NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
|
||||
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
||||
if (caretAccessible) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
|
||||
caretAccessibleIface->AddDocSelectionListener(domDoc);
|
||||
caretAccessible->AddDocSelectionListener(domDoc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,12 +693,10 @@ nsresult nsDocAccessible::RemoveEventListeners()
|
||||
|
||||
nsRefPtr<nsRootAccessible> rootAccessible(GetRootAccessible());
|
||||
if (rootAccessible) {
|
||||
nsCOMPtr<nsIAccessible> caretAccessible;
|
||||
rootAccessible->GetCaretAccessible(getter_AddRefs(caretAccessible));
|
||||
nsCOMPtr<nsIAccessibleCaret> caretAccessibleIface(do_QueryInterface(caretAccessible));
|
||||
if (caretAccessibleIface) {
|
||||
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
||||
if (caretAccessible) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
|
||||
caretAccessibleIface->RemoveDocSelectionListener(domDoc);
|
||||
caretAccessible->RemoveDocSelectionListener(domDoc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,9 +38,7 @@
|
||||
// NOTE: alphabetically ordered
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessibleEventData.h"
|
||||
#include "nsCaretAccessible.h"
|
||||
#include "nsHTMLSelectAccessible.h"
|
||||
#include "nsIAccessibleCaret.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsICaret.h"
|
||||
#include "nsIDocShell.h"
|
||||
@ -321,11 +319,7 @@ nsresult nsRootAccessible::AddEventListeners()
|
||||
}
|
||||
|
||||
if (!mCaretAccessible) {
|
||||
mCaretAccessible = new nsCaretAccessible(mDOMNode, mWeakShell, this);
|
||||
nsCOMPtr<nsPIAccessNode> accessNode(do_QueryInterface(mCaretAccessible));
|
||||
if (accessNode && NS_FAILED(accessNode->Init())) {
|
||||
mCaretAccessible = nsnull;
|
||||
}
|
||||
mCaretAccessible = new nsCaretAccessible(this);
|
||||
}
|
||||
|
||||
// Fire accessible focus event for pre-existing focus, but wait until all internal
|
||||
@ -356,23 +350,21 @@ nsresult nsRootAccessible::RemoveEventListeners()
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), this, PR_TRUE);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIAccessNode> caretAccessNode(do_QueryInterface(mCaretAccessible));
|
||||
if (caretAccessNode) {
|
||||
caretAccessNode->Shutdown();
|
||||
if (mCaretAccessible) {
|
||||
mCaretAccessible->Shutdown();
|
||||
mCaretAccessible = nsnull;
|
||||
}
|
||||
|
||||
return nsDocAccessible::RemoveEventListeners();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsRootAccessible::GetCaretAccessible(nsIAccessible **aCaretAccessible)
|
||||
already_AddRefed<nsCaretAccessible>
|
||||
nsRootAccessible::GetCaretAccessible()
|
||||
{
|
||||
*aCaretAccessible = nsnull;
|
||||
if (mCaretAccessible) {
|
||||
CallQueryInterface(mCaretAccessible, aCaretAccessible);
|
||||
NS_ADDREF(NS_STATIC_CAST(nsISupports*, mCaretAccessible));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return mCaretAccessible;
|
||||
}
|
||||
|
||||
void nsRootAccessible::TryFireEarlyLoadEvent(nsIDOMNode *aDocNode)
|
||||
|
@ -41,11 +41,11 @@
|
||||
#include "nsDocAccessibleWrap.h"
|
||||
#include "nsHashtable.h"
|
||||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsCaretAccessible.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFocusListener.h"
|
||||
#include "nsIDOMFormListener.h"
|
||||
#include "nsIDOMXULListener.h"
|
||||
#include "nsIAccessibleCaret.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
#define NS_ROOTACCESSIBLE_IMPL_CID \
|
||||
@ -81,9 +81,6 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
// ----- nsIDOMEventListener --------------------------
|
||||
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
// nsIAccessibleDocument
|
||||
NS_IMETHOD GetCaretAccessible(nsIAccessible **aAccessibleCaret);
|
||||
|
||||
// nsIAccessNode
|
||||
NS_IMETHOD Init();
|
||||
NS_IMETHOD Shutdown();
|
||||
@ -106,6 +103,8 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
nsIDOMEvent *aFocusEvent,
|
||||
PRBool aForceEvent = PR_FALSE);
|
||||
|
||||
already_AddRefed<nsCaretAccessible> GetCaretAccessible();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsITimer> mFireFocusTimer;
|
||||
static void FireFocusCallback(nsITimer *aTimer, void *aClosure);
|
||||
@ -124,7 +123,7 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
#endif
|
||||
already_AddRefed<nsIDocShellTreeItem>
|
||||
GetContentDocShell(nsIDocShellTreeItem *aStart);
|
||||
nsCOMPtr<nsIAccessibleCaret> mCaretAccessible;
|
||||
nsRefPtr<nsCaretAccessible> mCaretAccessible;
|
||||
PRPackedBool mIsInDHTMLMenu;
|
||||
};
|
||||
|
||||
|
@ -879,7 +879,8 @@ NS_IMETHODIMP nsHyperTextAccessible::GetRangeExtents(PRInt32 aStartOffset, PRInt
|
||||
{
|
||||
nsIntRect boundsRect;
|
||||
nsIFrame *endFrameUnused;
|
||||
if (!GetPosAndText(aStartOffset, aEndOffset, nsnull, &endFrameUnused, &boundsRect)) {
|
||||
if (!GetPosAndText(aStartOffset, aEndOffset, nsnull, &endFrameUnused, &boundsRect) ||
|
||||
boundsRect.IsEmpty()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -531,6 +531,7 @@ void nsAccessNodeWrap::InitAccessibility()
|
||||
void nsAccessNodeWrap::ShutdownAccessibility()
|
||||
{
|
||||
NS_IF_RELEASE(gTextEvent);
|
||||
::DestroyCaret();
|
||||
|
||||
if (!gIsAccessibilityActive) {
|
||||
return;
|
||||
|
@ -1511,15 +1511,9 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
|
||||
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(accessible));
|
||||
NS_ENSURE_STATE(accessNode);
|
||||
|
||||
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
||||
// Fire additional old-style MSAA caret events as well as the IA2 event
|
||||
nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
|
||||
if (rootAccessible) {
|
||||
nsCOMPtr<nsIAccessible> caretAccessible;
|
||||
void* handle = nsnull;
|
||||
rootAccessible->GetWindowHandle(&handle);
|
||||
NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, (HWND)handle, OBJID_CARET, CHILDID_SELF);
|
||||
}
|
||||
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
|
||||
eventType == nsIAccessibleEvent::EVENT_FOCUS) {
|
||||
UpdateSystemCaret();
|
||||
}
|
||||
|
||||
PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
|
||||
@ -1664,3 +1658,38 @@ void nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessibl
|
||||
NS_IF_ADDREF(*aXPAccessible);
|
||||
}
|
||||
|
||||
void nsAccessibleWrap::UpdateSystemCaret()
|
||||
{
|
||||
// Move the system caret so that Windows Tablet Edition and tradional ATs with
|
||||
// off-screen model can follow the caret
|
||||
::DestroyCaret();
|
||||
|
||||
nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
|
||||
if (!rootAccessible) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
||||
if (!caretAccessible) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIWidget *widget;
|
||||
nsRect caretRect = caretAccessible->GetCaretRect(&widget);
|
||||
HWND caretWnd;
|
||||
if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create invisible bitmap for caret, otherwise its appearance interferes
|
||||
// with Gecko caret
|
||||
HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, NULL);
|
||||
if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
|
||||
::ShowCaret(caretWnd);
|
||||
RECT windowRect;
|
||||
::GetWindowRect(caretWnd, &windowRect);
|
||||
::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
|
||||
::DeleteObject(caretBitMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,6 +296,15 @@ class nsAccessibleWrap : public nsAccessible,
|
||||
// Helper methods
|
||||
static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
|
||||
|
||||
/**
|
||||
* System caret support: update the Windows caret position.
|
||||
* The system caret works more universally than the MSAA caret
|
||||
* For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
|
||||
* We will use an invisible system caret.
|
||||
* Gecko is still responsible for drawing its own caret
|
||||
*/
|
||||
void UpdateSystemCaret();
|
||||
|
||||
virtual void GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessible **aXPAccessible);
|
||||
NS_IMETHOD GetNativeInterface(void **aOutAccessible);
|
||||
|
||||
|
@ -5223,30 +5223,19 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
||||
#ifdef ACCESSIBILITY
|
||||
case WM_GETOBJECT:
|
||||
{
|
||||
LRESULT lAcc = 0;
|
||||
IAccessible *msaaAccessible = NULL;
|
||||
*aRetValue = 0;
|
||||
if (lParam == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
|
||||
nsCOMPtr<nsIAccessible> rootAccessible = GetRootAccessible(); // Held by a11y cache
|
||||
if (rootAccessible) {
|
||||
IAccessible *msaaAccessible = NULL;
|
||||
rootAccessible->GetNativeInterface((void**)&msaaAccessible); // does an addref
|
||||
}
|
||||
}
|
||||
else if (lParam == OBJID_CARET) { // each root accessible owns a caret accessible
|
||||
nsCOMPtr<nsIAccessible> rootAccessible = GetRootAccessible(); // Held by a11y cache
|
||||
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(rootAccessible));
|
||||
if (accDoc) {
|
||||
nsCOMPtr<nsIAccessible> accessibleCaret;
|
||||
accDoc->GetCaretAccessible(getter_AddRefs(accessibleCaret));
|
||||
if (accessibleCaret) {
|
||||
accessibleCaret->GetNativeInterface((void**)&msaaAccessible);
|
||||
if (msaaAccessible) {
|
||||
*aRetValue = LresultFromObject(IID_IAccessible, wParam, msaaAccessible); // does an addref
|
||||
msaaAccessible->Release(); // release extra addref
|
||||
result = PR_TRUE; // We handled the WM_GETOBJECT message
|
||||
}
|
||||
}
|
||||
}
|
||||
if (msaaAccessible) {
|
||||
lAcc = LresultFromObject(IID_IAccessible, wParam, msaaAccessible); // does an addref
|
||||
msaaAccessible->Release(); // release extra addref
|
||||
}
|
||||
return (*aRetValue = lAcc) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user