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:
aaronleventhal@moonset.net 2007-06-14 10:12:50 -07:00
parent 1423e531e9
commit cf34398740
14 changed files with 206 additions and 302 deletions

View File

@ -59,7 +59,6 @@ XPIDLSRCS = \
nsIAccessibleRole.idl \
nsIAccessibleStates.idl \
nsPIAccessible.idl \
nsIAccessibleCaret.idl \
nsIAccessibleDocument.idl \
nsPIAccessibleDocument.idl \
nsIAccessibleProvider.idl \

View File

@ -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);
};

View File

@ -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.
*/

View File

@ -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()

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;
};

View File

@ -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;
}

View File

@ -531,6 +531,7 @@ void nsAccessNodeWrap::InitAccessibility()
void nsAccessNodeWrap::ShutdownAccessibility()
{
NS_IF_RELEASE(gTextEvent);
::DestroyCaret();
if (!gIsAccessibilityActive) {
return;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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