From c4c823f375f214ed3318cc7f137bf0e725239169 Mon Sep 17 00:00:00 2001 From: "benjamin%smedbergs.us" Date: Thu, 15 Jun 2006 15:22:23 +0000 Subject: [PATCH] Reverting bug 340825 for aaronlev due to a busted checkin --- accessible/public/nsIAccessible.idl | 2 +- accessible/public/nsIAccessibleValue.idl | 10 +- accessible/src/atk/nsAccessibleHyperText.cpp | 538 +++++++ accessible/src/atk/nsAccessibleHyperText.h | 76 + accessible/src/atk/nsAccessibleText.cpp | 1420 +++++++++++++++++ accessible/src/atk/nsAccessibleText.h | 136 ++ accessible/src/atk/nsHTMLBlockAccessible.cpp | 76 + accessible/src/atk/nsHTMLBlockAccessible.h | 59 + accessible/src/atk/nsMaiInterfaceValue.cpp | 33 +- accessible/src/atk/nsMaiInterfaceValue.h | 3 - .../atk/nsXULFormControlAccessibleWrap.cpp | 56 + .../src/atk/nsXULFormControlAccessibleWrap.h | 11 + accessible/src/base/nsAccessibilityAtomList.h | 2 - .../src/base/nsAccessibilityService.cpp | 2 +- accessible/src/base/nsAccessible.cpp | 123 +- accessible/src/base/nsAccessible.h | 9 +- accessible/src/base/nsRootAccessible.cpp | 2 +- accessible/src/mac/nsAccessibleWrap.h | 1 + accessible/src/msaa/nsAccessibleWrap.cpp | 2 +- accessible/src/msaa/nsAccessibleWrap.h | 1 + accessible/src/other/nsAccessibleWrap.h | 1 + .../src/xul/nsXULFormControlAccessible.cpp | 53 +- .../src/xul/nsXULFormControlAccessible.h | 7 +- 23 files changed, 2416 insertions(+), 207 deletions(-) create mode 100644 accessible/src/atk/nsAccessibleHyperText.cpp create mode 100644 accessible/src/atk/nsAccessibleHyperText.h create mode 100644 accessible/src/atk/nsAccessibleText.cpp create mode 100644 accessible/src/atk/nsAccessibleText.h create mode 100644 accessible/src/atk/nsHTMLBlockAccessible.cpp create mode 100644 accessible/src/atk/nsHTMLBlockAccessible.h diff --git a/accessible/public/nsIAccessible.idl b/accessible/public/nsIAccessible.idl index 0980f5d2c60f..57597a15e23b 100644 --- a/accessible/public/nsIAccessible.idl +++ b/accessible/public/nsIAccessible.idl @@ -100,7 +100,7 @@ interface nsIAccessible : nsISupports * Accessible value -- a number or a secondary text equivalent for this node * Widgets that use xhtml2:role can force a value using the valuenow attribute */ - readonly attribute AString value; + readonly attribute AString finalValue; /** * Accessible description -- long text associated with this node diff --git a/accessible/public/nsIAccessibleValue.idl b/accessible/public/nsIAccessibleValue.idl index 80bc62a3967e..8dbd9d47e5e6 100644 --- a/accessible/public/nsIAccessibleValue.idl +++ b/accessible/public/nsIAccessibleValue.idl @@ -44,8 +44,14 @@ interface nsIAccessibleValue : nsISupports { readonly attribute double maximumValue; readonly attribute double minimumValue; - attribute double currentValue; - readonly attribute double minimumIncrement; + readonly attribute double currentValue; + + /** + * We want to be able to return a success condition of the value + * getting set. ie if the value is not within the interval of + * minimumValue-maximumValue + */ + boolean setCurrentValue (in double value); }; /* diff --git a/accessible/src/atk/nsAccessibleHyperText.cpp b/accessible/src/atk/nsAccessibleHyperText.cpp new file mode 100644 index 000000000000..a3a1ee3b2003 --- /dev/null +++ b/accessible/src/atk/nsAccessibleHyperText.cpp @@ -0,0 +1,538 @@ +/* -*- 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Yuan (kyle.yuan@sun.com) + * Ginn Chen (ginn.chen@sun.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 "nsAccessibilityAtoms.h" +#include "nsAccessibilityService.h" +#include "nsAccessibleHyperText.h" +#include "nsHTMLLinkAccessibleWrap.h" +#include "nsHTMLTextAccessible.h" +#include "nsPIAccessNode.h" +#include "nsIFrame.h" +#include "nsILink.h" +#include "nsIServiceManager.h" +#include "nsArrayUtils.h" + +/* + * nsAccessibleHyperText supports both nsIAccessibleHyperText and nsIAccessibleText. + * It's mainly aimed at the compound content that consists of many text nodes and links. + * Typically, it's a paragraph of text, a cell of table, etc. +*/ + +NS_IMPL_ISUPPORTS2(nsAccessibleHyperText, nsIAccessibleHyperText, nsIAccessibleText) + +nsAccessibleHyperText::nsAccessibleHyperText(nsIDOMNode* aDomNode, nsIWeakReference* aShell) +{ + mIndex = -1; + nsCOMPtr content(do_QueryInterface(aDomNode)); + if (content) { + nsCOMPtr parentContent = content->GetParent(); + if (parentContent) + mIndex = parentContent->IndexOf(content); + } + + nsCOMPtr shell(do_QueryReferent(aShell)); + if (shell) { + mTextChildren = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (mTextChildren) { + nsCOMPtr content(do_QueryInterface(aDomNode)); + nsIFrame *frame = shell->GetPrimaryFrameFor(content); + nsIFrame *parentFrame = nsAccessible::GetParentBlockFrame(frame); + NS_ASSERTION(parentFrame, "Error: HyperText can't get parent block frame"); + if (parentFrame) { + nsIFrame* childFrame = parentFrame->GetFirstChild(nsnull); + PRBool bSave = PR_FALSE; + GetAllTextChildren(shell->GetPresContext(), childFrame, + aDomNode, bSave); + } + } + } +} + +void nsAccessibleHyperText::Shutdown() +{ + mTextChildren = nsnull; +} + +PRBool nsAccessibleHyperText::GetAllTextChildren(nsPresContext *aPresContext, nsIFrame *aCurFrame, nsIDOMNode* aNode, PRBool &bSave) +{ + while (aCurFrame) { + + nsIAtom* frameType = aCurFrame->GetType(); + if (frameType == nsAccessibilityAtoms::blockFrame) { + if (bSave) + return PR_TRUE; + } + else { + if (frameType == nsAccessibilityAtoms::textFrame) { + // Skip the empty text frames that usually only consist of "\n" + if (! aCurFrame->GetRect().IsEmpty()) { + nsCOMPtr node(do_QueryInterface(aCurFrame->GetContent())); + if (bSave || node == aNode) { + // some long text node may be divided into several frames, + // so we must check whether this node is already in the array + PRUint32 index; + nsresult rv = mTextChildren->IndexOf(0, node, &index); + if (NS_FAILED(rv)) { + mTextChildren->AppendElement(node, PR_FALSE); + } + bSave = PR_TRUE; + } + } + } + + nsIFrame* childFrame = aCurFrame->GetFirstChild(nsnull); + if (GetAllTextChildren(aPresContext, childFrame, aNode, bSave)) + return PR_TRUE; + } + + nsIFrame* siblingFrame = aCurFrame->GetNextSibling(); + aCurFrame = siblingFrame; + } + return PR_FALSE; +} + +PRInt32 nsAccessibleHyperText::GetIndex() +// XXX, this index is used for giving a hypertext a meaningful name, such as "Paragraph n", +// but by now, we haven't found a better way to do that, just use the index of our parent's +// children list as the number. +{ + return mIndex; +} + +nsIDOMNode* nsAccessibleHyperText::FindTextNodeByOffset(PRInt32 aOffset, PRInt32& aBeforeLength) +{ + aBeforeLength = 0; + + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsAccessibleText accText(domNode); + PRInt32 charCount; + if (NS_SUCCEEDED(accText.GetCharacterCount(&charCount))) { + if (aOffset >= 0 && aOffset <= charCount) { + return domNode; + } + aOffset -= charCount; + aBeforeLength += charCount; + } + } + + return nsnull; +} + +nsresult nsAccessibleHyperText::GetTextHelper(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType, + PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + PRInt32 beforeLength; + nsIDOMNode* domNode = FindTextNodeByOffset(aOffset, beforeLength); + if (domNode) { + nsAccessibleText accText(domNode); + // call nsAccessibleText::GetTextHelper directly so that it can adjust the aStartOffset/aEndOffset + // according to the mTextChildren + nsresult rv = accText.GetTextHelper(aType, aBoundaryType, aOffset - beforeLength, aStartOffset, aEndOffset, mTextChildren, aText); + return rv; + } + + return NS_ERROR_INVALID_ARG; +} + +// ------- nsIAccessibleText --------------- +/* attribute long caretOffset; */ +NS_IMETHODIMP nsAccessibleHyperText::GetCaretOffset(PRInt32 *aCaretOffset) +{ + *aCaretOffset = 0; + + PRInt32 charCount, caretOffset; + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsAccessibleText accText(domNode); + if (NS_SUCCEEDED(accText.GetCaretOffset(&caretOffset))) { + *aCaretOffset += caretOffset; + return NS_OK; + } else if (GetLinkNode(domNode) == nsAccessNode::gLastFocusedNode) { + //Focus is here + return NS_OK; + } + if (NS_SUCCEEDED(accText.GetCharacterCount(&charCount))) { + *aCaretOffset += charCount; + } + } + + // The current focus node is not inside us + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsAccessibleHyperText::SetCaretOffset(PRInt32 aCaretOffset) +{ + PRInt32 beforeLength; + nsIDOMNode* domNode = FindTextNodeByOffset(aCaretOffset, beforeLength); + if (domNode) { + nsAccessibleText accText(domNode); + return accText.SetCaretOffset(aCaretOffset - beforeLength); + } + + return NS_ERROR_INVALID_ARG; +} + +/* readonly attribute long characterCount; */ +NS_IMETHODIMP nsAccessibleHyperText::GetCharacterCount(PRInt32 *aCharacterCount) +{ + *aCharacterCount = 0; + + PRInt32 charCount; + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsAccessibleText accText(domNode); + if (NS_SUCCEEDED(accText.GetCharacterCount(&charCount))) + *aCharacterCount += charCount; + } + + return NS_OK; +} + +/* readonly attribute long selectionCount; */ +NS_IMETHODIMP nsAccessibleHyperText::GetSelectionCount(PRInt32 *aSelectionCount) +{ + *aSelectionCount = 0; + + PRInt32 selCount; + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsAccessibleText accText(domNode); + if (NS_SUCCEEDED(accText.GetSelectionCount(&selCount))) + *aSelectionCount += selCount; + } + + return NS_OK; +} + +/* AString getText (in long startOffset, in long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString & aText) +{ + if (aEndOffset == -1) + GetCharacterCount(&aEndOffset); + + PRInt32 charCount, totalCount = 0, currentStart, currentEnd; + PRUint32 index, count; + nsAutoString text, nodeText; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsAccessibleText accText(domNode); + if (NS_SUCCEEDED(accText.GetCharacterCount(&charCount))) { + currentStart = aStartOffset - totalCount; + currentEnd = aEndOffset - totalCount; + if (currentStart >= 0 && currentStart < charCount) { + accText.GetText(currentStart, NS_MIN(charCount, currentEnd), nodeText); + text += nodeText; + aStartOffset += charCount - currentStart; + if (aStartOffset >= aEndOffset) + break; + } + totalCount += charCount; + } + } + + // Eliminate the new line character + PRInt32 start = 0, length = text.Length(); + PRInt32 offset = text.FindCharInSet("\n\r"); + while (offset != kNotFound) { + if (offset > start) + aText += Substring(text, start, offset - start); + + start = offset + 1; + offset = text.FindCharInSet("\n\r", start); + } + // Consume the last bit of the string if there's any left + if (start < length) { + if (start) + aText += Substring(text, start, length - start); + else + aText = text; + } + + return NS_OK; +} + +/* AString getTextBeforeOffset (in long offset, in nsAccessibleTextBoundary boundaryType, out long startOffset, out long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText); +} + +/* AString getTextAfterOffset (in long offset, in nsAccessibleTextBoundary boundaryType, out long startOffset, out long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText); +} + +/* AString getTextAtOffset (in long offset, in nsAccessibleTextBoundary boundaryType, out long startOffset, out long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText); +} + +/* wchar getCharacterAtOffset (in long offset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter) +{ + PRInt32 beforeLength; + nsIDOMNode* domNode = FindTextNodeByOffset(aOffset, beforeLength); + if (domNode) { + nsAccessibleText accText(domNode); + return accText.GetCharacterAtOffset(aOffset - beforeLength, aCharacter); + } + + return NS_ERROR_INVALID_ARG; +} + +/* nsISupports getAttributeRange (in long offset, out long rangeStartOffset, out long rangeEndOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetAttributeRange(PRInt32 aOffset, PRInt32 *aRangeStartOffset, PRInt32 *aRangeEndOffset, nsISupports **aAttributes) +{ + *aRangeStartOffset = aOffset; + GetCharacterCount(aRangeEndOffset); + *aAttributes = 0; + return NS_OK; +} + +/* void getCharacterExtents (in long offset, out long x, out long y, out long width, out long height, in nsAccessibleCoordType coordType); */ +NS_IMETHODIMP nsAccessibleHyperText::GetCharacterExtents(PRInt32 aOffset, PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight, nsAccessibleCoordType aCoordType) +{ + PRInt32 beforeLength; + nsIDOMNode* domNode = FindTextNodeByOffset(aOffset, beforeLength); + if (domNode) { + nsAccessibleText accText(domNode); + return accText.GetCharacterExtents(aOffset - beforeLength, aX, aY, aWidth, aHeight, aCoordType); + } + + return NS_ERROR_INVALID_ARG; +} + +/* long getOffsetAtPoint (in long x, in long y, in nsAccessibleCoordType coordType); */ +NS_IMETHODIMP nsAccessibleHyperText::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY, nsAccessibleCoordType aCoordType, PRInt32 *aOffset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void getSelectionBounds (in long selectionNum, out long startOffset, out long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::GetSelectionBounds(PRInt32 aSelectionNum, PRInt32 *aStartOffset, PRInt32 *aEndOffset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void setSelectionBounds (in long selectionNum, in long startOffset, in long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::SetSelectionBounds(PRInt32 aSelectionNum, PRInt32 aStartOffset, PRInt32 aEndOffset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void addSelection (in long startOffset, in long endOffset); */ +NS_IMETHODIMP nsAccessibleHyperText::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void removeSelection (in long selectionNum); */ +NS_IMETHODIMP nsAccessibleHyperText::RemoveSelection(PRInt32 aSelectionNum) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// ------- nsIAccessibleHyperText --------------- +/* readonly attribute long links; */NS_IMETHODIMP nsAccessibleHyperText::GetLinks(PRInt32 *aLinks) +{ + *aLinks = 0; + + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + if (GetLinkNode(domNode)) { + (*aLinks)++; + } + } + + return NS_OK; +} + +/* nsIAccessibleHyperLink getLink (in long index); */ +NS_IMETHODIMP nsAccessibleHyperText::GetLink(PRInt32 aIndex, nsIAccessibleHyperLink **aLink) +{ + PRUint32 index, count, linkCount = 0; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsIDOMNode* parentNode = GetLinkNode(domNode); + if (parentNode) { + if (linkCount++ == NS_STATIC_CAST(PRUint32, aIndex)) { + nsCOMPtr weakShell; + nsAccessibilityService::GetShellFromNode(parentNode, getter_AddRefs(weakShell)); + NS_ENSURE_TRUE(weakShell, NS_ERROR_FAILURE); + + // Check to see if we already have it in the cache. + nsCOMPtr + accService(do_GetService("@mozilla.org/accessibilityService;1")); + NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE); + + nsCOMPtr cachedAcc; + nsresult rv = accService->GetCachedAccessible(parentNode, weakShell, + getter_AddRefs(cachedAcc)); + NS_ENSURE_SUCCESS(rv, rv); + *aLink = nsnull; + if (cachedAcc) { + // Retrieved from cache + nsCOMPtr cachedLink(do_QueryInterface(cachedAcc)); + if (cachedLink) { + *aLink = cachedLink; + NS_IF_ADDREF(*aLink); + } + } + if (!(*aLink)) { + *aLink = new nsHTMLLinkAccessibleWrap(parentNode, mTextChildren, weakShell, nsnull); + NS_ENSURE_TRUE(*aLink, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*aLink); + nsCOMPtr accessNode(do_QueryInterface(*aLink)); + accessNode->Init(); + } + break; + } + } + } + + return NS_OK; +} + +/* long getLinkIndex (in long charIndex); */ +NS_IMETHODIMP nsAccessibleHyperText::GetLinkIndex(PRInt32 aCharIndex, PRInt32 *aLinkIndex) +{ + *aLinkIndex = -1; + PRInt32 beforeLength_unused; + PRUint32 nodeIndex; + nsIDOMNode* domNode = FindTextNodeByOffset(aCharIndex, beforeLength_unused); + if (GetLinkNode(domNode) + && NS_SUCCEEDED(mTextChildren->IndexOf(0, domNode, &nodeIndex))) { + (*aLinkIndex)++; + for (PRUint32 index = 0; index < nodeIndex; index++) { + nsCOMPtr childNode(do_QueryElementAt(mTextChildren, index)); + if (GetLinkNode(childNode)) { + (*aLinkIndex)++; + } + } + } + return NS_OK; +} + +/* long getSelectedLinkIndex (); */ +NS_IMETHODIMP nsAccessibleHyperText::GetSelectedLinkIndex(PRInt32 *aSelectedLinkIndex) +{ + *aSelectedLinkIndex = -1; + + PRUint32 count; + mTextChildren->GetLength(&count); + if (count <= 0) + return NS_ERROR_FAILURE; + + nsCOMPtr curNode(do_QueryElementAt(mTextChildren, 0)); + + PRUint32 index, linkCount = 0; + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsIDOMNode* linkNode = GetLinkNode(domNode); + if (linkNode) { + if (linkNode == nsAccessNode::gLastFocusedNode) { + *aSelectedLinkIndex = linkCount; + return NS_OK; + } + linkCount++; + } + } + + return NS_ERROR_FAILURE; +} + +nsresult nsAccessibleHyperText::GetBounds(nsIWeakReference *aWeakShell, PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height) +{ + *x = *y = *width = *height = 0; + + nsRect unionRectTwips; + PRUint32 index, count; + mTextChildren->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(mTextChildren, index)); + nsHTMLTextAccessible *accText = new nsHTMLTextAccessible(domNode, aWeakShell, nsnull); + if (!accText) + return NS_ERROR_OUT_OF_MEMORY; + + nsRect frameRect; + accText->GetBounds(&frameRect.x, &frameRect.y, &frameRect.width, &frameRect.height); + unionRectTwips.UnionRect(unionRectTwips, frameRect); + delete accText; + } + + *x = unionRectTwips.x; + *y = unionRectTwips.y; + *width = unionRectTwips.width; + *height = unionRectTwips.height; + + return NS_OK; +} + +nsIDOMNode* nsAccessibleHyperText::GetLinkNode(nsIDOMNode* aNode) +{ + + nsCOMPtr parentNode; + nsCOMPtr link; + while (aNode && link == nsnull) { + // text node maybe a child (or grandchild, ...) of a link node + aNode->GetParentNode(getter_AddRefs(parentNode)); + aNode = parentNode; + link = do_QueryInterface(parentNode); + } + + return parentNode; +} diff --git a/accessible/src/atk/nsAccessibleHyperText.h b/accessible/src/atk/nsAccessibleHyperText.h new file mode 100644 index 000000000000..f18b77c439f3 --- /dev/null +++ b/accessible/src/atk/nsAccessibleHyperText.h @@ -0,0 +1,76 @@ +/* -*- 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Yuan (kyle.yuan@sun.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +#ifndef _nsAccessibleHyperText_H_ +#define _nsAccessibleHyperText_H_ + +#include "nsAccessibleText.h" +#include "nsIAccessibleHyperText.h" +#include "nsIAccessibleText.h" +#include "nsIMutableArray.h" +#include "nsTextAccessible.h" + +class nsAccessibleHyperText : public nsIAccessibleHyperText, + public nsIAccessibleText +{ + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBLEHYPERTEXT + NS_DECL_NSIACCESSIBLETEXT + +public: + nsAccessibleHyperText(nsIDOMNode* aDomNode, nsIWeakReference* aShell); + virtual ~nsAccessibleHyperText() {}; + + nsresult GetBounds(nsIWeakReference *aShell, PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height); + void Shutdown(); + PRInt32 GetIndex(); + +protected: + nsCOMPtr mTextChildren; + PRInt32 mIndex; + + PRBool GetAllTextChildren(nsPresContext *aPresContext, nsIFrame *aCurFrame, nsIDOMNode* aNode, PRBool &bSave); + nsIDOMNode* FindTextNodeByOffset(PRInt32 aOffset, PRInt32& aBeforeLength); + nsresult GetTextHelper(EGetTextType aType, + nsAccessibleTextBoundary aBoundaryType, + PRInt32 aOffset, PRInt32 *aStartOffset, + PRInt32 *aEndOffset, nsAString & aText); + nsIDOMNode* GetLinkNode(nsIDOMNode* aNode); +}; + +#endif diff --git a/accessible/src/atk/nsAccessibleText.cpp b/accessible/src/atk/nsAccessibleText.cpp new file mode 100644 index 000000000000..d4df3be40d9f --- /dev/null +++ b/accessible/src/atk/nsAccessibleText.cpp @@ -0,0 +1,1420 @@ +/* -*- 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Yuan (kyle.yuan@sun.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +// NOTE: alphabetically ordered +#include "nsAccessibleText.h" +#include "nsContentCID.h" +#include "nsIClipboard.h" +#include "nsIDOMAbstractView.h" +#include "nsIDOMCharacterData.h" +#include "nsIDOMDocument.h" +#include "nsIDOMDocumentView.h" +#include "nsIDOMHTMLBRElement.h" +#include "nsIDOMHTMLDivElement.h" +#include "nsIDOMHTMLInputElement.h" +#include "nsIDOMHTMLTextAreaElement.h" +#include "nsIDOMRange.h" +#include "nsIDOMText.h" +#include "nsIDOMWindow.h" +#include "nsIDOMWindowInternal.h" +#include "nsIDeviceContext.h" +#include "nsIDocument.h" +#include "nsIDocumentEncoder.h" +#include "nsIFontMetrics.h" +#include "nsIFrame.h" +#include "nsIPlaintextEditor.h" +#include "nsIRenderingContext.h" +#include "nsITextContent.h" +#include "nsIWidget.h" +#include "nsStyleStruct.h" +#include "nsTextFragment.h" +#include "nsIMutableArray.h" +#include "nsArrayUtils.h" +#include "nsIComponentManager.h" + +static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); + +// -------------------------------------------------------- +// nsAccessibleText Accessible +// -------------------------------------------------------- +PRBool nsAccessibleText::gSuppressedNotifySelectionChanged = PR_FALSE; + +NS_IMPL_ISUPPORTS1(nsAccessibleText, nsIAccessibleText) + +/** + * nsAccessibleText implements the nsIAccessibleText interface for static text which mTextNode + * has nsITextContent interface + */ +nsAccessibleText::nsAccessibleText(nsIDOMNode *aNode) +{ + mTextNode = aNode; +} + +/** + * nsIAccessibleText helpers + */ +nsresult nsAccessibleText::GetSelections(nsISelectionController **aSelCon, nsISelection **aDomSel) +{ + nsCOMPtr domDoc; + mTextNode->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc(do_QueryInterface(domDoc)); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + nsIPresShell *shell = doc->GetShellAt(0); + NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); + + nsCOMPtr content(do_QueryInterface(mTextNode)); + nsIFrame *frame = shell->GetPrimaryFrameFor(content); + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); + + // Get the selection and selection controller + nsCOMPtr selCon; + nsCOMPtr domSel; + frame->GetSelectionController(shell->GetPresContext(), + getter_AddRefs(selCon)); + if (selCon) + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel)); + + NS_ENSURE_TRUE(selCon && domSel, NS_ERROR_FAILURE); + + PRBool isSelectionCollapsed; + domSel->GetIsCollapsed(&isSelectionCollapsed); + // Don't perform any actions when the selection is not collapsed + NS_ENSURE_TRUE(isSelectionCollapsed, NS_ERROR_FAILURE); + + if (aSelCon) { + *aSelCon = selCon; + NS_ADDREF(*aSelCon); + } + + if (aDomSel) { + *aDomSel = domSel; + NS_ADDREF(*aDomSel); + } + + return NS_OK; +} + +nsresult nsAccessibleText::DOMPointToOffset(nsISupports *aClosure, nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32* aResult) +{ + NS_ENSURE_ARG_POINTER(aNode && aResult); + + *aResult = aNodeOffset; + + nsCOMPtr domNodeArray(do_QueryInterface(aClosure)); + if (domNodeArray) { + // Static text, calculate the offset from a given set of (text) node + PRUint32 textLength, totalLength = 0; + PRUint32 index, count; + domNodeArray->GetLength(&count); + for (index = 0; index < count; index++) { + nsCOMPtr domNode(do_QueryElementAt(domNodeArray, index)); + if (aNode == domNode) { + *aResult = aNodeOffset + totalLength; + break; + } + nsCOMPtr domText(do_QueryInterface(domNode)); + if (domText) { + domText->GetLength(&textLength); + totalLength += textLength; + } + } + + return NS_OK; + } + + nsCOMPtr editor(do_QueryInterface(aClosure)); + if (editor) { // revised according to nsTextControlFrame::DOMPointToOffset + // Editable text, calculate the offset from the editor + nsCOMPtr rootElement; + editor->GetRootElement(getter_AddRefs(rootElement)); + nsCOMPtr rootNode(do_QueryInterface(rootElement)); + + NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE); + + nsCOMPtr nodeList; + + nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); + + PRUint32 length = 0; + rv = nodeList->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + if (!length || aNodeOffset < 0) + return NS_OK; + + PRInt32 i, textOffset = 0; + PRInt32 lastIndex = (PRInt32)length - 1; + + for (i = 0; i < (PRInt32)length; i++) { + if (rootNode == aNode && i == aNodeOffset) { + *aResult = textOffset; + return NS_OK; + } + + nsCOMPtr item; + rv = nodeList->Item(i, getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(item, NS_ERROR_FAILURE); + + if (item == aNode) { + *aResult = textOffset + aNodeOffset; + return NS_OK; + } + + nsCOMPtr domText(do_QueryInterface(item)); + + if (domText) { + PRUint32 textLength = 0; + + rv = domText->GetLength(&textLength); + NS_ENSURE_SUCCESS(rv, rv); + + textOffset += textLength; + } + else { + // Must be a BR node. If it's not the last BR node + // under the root, count it as a newline. + if (i != lastIndex) + ++textOffset; + } + } + + NS_ASSERTION((aNode == rootNode && aNodeOffset == (PRInt32)length), + "Invalid node offset!"); + + *aResult = textOffset; + } + + return NS_OK; +} + +nsresult nsAccessibleText::OffsetToDOMPoint(nsISupports *aClosure, PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition) +{ + NS_ENSURE_ARG_POINTER(aResult && aPosition); + + *aResult = nsnull; + *aPosition = 0; + + nsCOMPtr editor(do_QueryInterface(aClosure)); + if (editor) { // revised according to nsTextControlFrame::OffsetToDOMPoint + nsCOMPtr rootElement; + editor->GetRootElement(getter_AddRefs(rootElement)); + nsCOMPtr rootNode(do_QueryInterface(rootElement)); + + NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE); + + nsCOMPtr nodeList; + + nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); + + PRUint32 length = 0; + + rv = nodeList->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + if (!length || aOffset < 0) { + *aPosition = 0; + *aResult = rootNode; + NS_ADDREF(*aResult); + return NS_OK; + } + + PRInt32 textOffset = 0; + PRUint32 lastIndex = length - 1; + + for (PRUint32 i=0; i item; + rv = nodeList->Item(i, getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(item, NS_ERROR_FAILURE); + + nsCOMPtr domText(do_QueryInterface(item)); + + if (domText) { + PRUint32 textLength = 0; + + rv = domText->GetLength(&textLength); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if aOffset falls within this range. + if (aOffset >= textOffset && aOffset <= textOffset+(PRInt32)textLength) { + *aPosition = aOffset - textOffset; + *aResult = item; + NS_ADDREF(*aResult); + return NS_OK; + } + + textOffset += textLength; + + // If there aren't any more siblings after this text node, + // return the point at the end of this text node! + + if (i == lastIndex) { + *aPosition = textLength; + *aResult = item; + NS_ADDREF(*aResult); + return NS_OK; + } + } + else { + // Must be a BR node, count it as a newline. + + if (aOffset == textOffset || i == lastIndex) { + // We've found the correct position, or aOffset takes us + // beyond the last child under rootNode, just return the point + // under rootNode that is in front of this br. + + *aPosition = i; + *aResult = rootNode; + NS_ADDREF(*aResult); + return NS_OK; + } + + ++textOffset; + } + } + } + + return NS_ERROR_FAILURE; +} + +nsresult nsAccessibleText::GetCurrentOffset(nsISupports *aClosure, nsISelection *aDomSel, PRInt32 *aOffset) +{ + nsCOMPtr focusNode; + aDomSel->GetFocusNode(getter_AddRefs(focusNode)); + aDomSel->GetFocusOffset(aOffset); + return DOMPointToOffset(aClosure, focusNode, *aOffset, aOffset); +} + +/* +Gets the specified text. + +aBoundaryType means: + +ATK_TEXT_BOUNDARY_CHAR + The character before/at/after the offset is returned. + +ATK_TEXT_BOUNDARY_WORD_START + The returned string is from the word start before/at/after the offset to the next word start. + +ATK_TEXT_BOUNDARY_WORD_END + The returned string is from the word end before/at/after the offset to the next work end. + +ATK_TEXT_BOUNDARY_SENTENCE_START + The returned string is from the sentence start before/at/after the offset to the next sentence start. + +ATK_TEXT_BOUNDARY_SENTENCE_END + The returned string is from the sentence end before/at/after the offset to the next sentence end. + +ATK_TEXT_BOUNDARY_LINE_START + The returned string is from the line start before/at/after the offset to the next line start. + +ATK_TEXT_BOUNDARY_LINE_END + The returned string is from the line end before/at/after the offset to the next line start. +*/ +nsresult nsAccessibleText::GetTextHelperCore(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType, + PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset, + nsISelectionController *aSelCon, nsISelection *aDomSel, + nsISupports *aClosure, nsAString &aText) +{ + PRInt32 rangeCount; + nsCOMPtr range, oldRange; + aDomSel->GetRangeCount(&rangeCount); + + if (rangeCount == 0) { // ever happen? + SetCaretOffset(aOffset); // a new range will be added here + rangeCount++; + } + aDomSel->GetRangeAt(rangeCount - 1, getter_AddRefs(range)); + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); + + // backup the original selection range to restore the selection status later + range->CloneRange(getter_AddRefs(oldRange)); + + // Step1: move caret to an appropriate start position + // Step2: move caret to end postion and select the text + PRBool isStep1Forward, isStep2Forward; // Moving directions for two steps + switch (aType) + { + case eGetBefore: + isStep1Forward = PR_FALSE; + isStep2Forward = PR_FALSE; + break; + case eGetAt: + isStep1Forward = PR_FALSE; + isStep2Forward = PR_TRUE; + break; + case eGetAfter: + isStep1Forward = PR_TRUE; + isStep2Forward = PR_TRUE; + break; + default: + return NS_ERROR_INVALID_ARG; + } + + // The start/end focus node may be not our mTextNode + nsCOMPtr startFocusNode, endFocusNode; + switch (aBoundaryType) + { + case BOUNDARY_CHAR: + if (aType == eGetAfter) { // We need the character next to current position + aSelCon->CharacterMove(isStep1Forward, PR_FALSE); + GetCurrentOffset(aClosure, aDomSel, aStartOffset); + } + aSelCon->CharacterMove(isStep2Forward, PR_TRUE); + GetCurrentOffset(aClosure, aDomSel, aEndOffset); + break; + case BOUNDARY_WORD_START: + { + PRBool dontMove = PR_FALSE; + // If we are at the word boundary, don't move the caret in the first step + if (aOffset == 0) + dontMove = PR_TRUE; + else { + PRUnichar prevChar; + GetCharacterAtOffset(aOffset - 1, &prevChar); + if (prevChar == ' ' || prevChar == '\t' || prevChar == '\n') + dontMove = PR_TRUE; + } + if (!dontMove) { + aSelCon->WordMove(isStep1Forward, PR_FALSE); // Move caret to previous/next word start boundary + GetCurrentOffset(aClosure, aDomSel, aStartOffset); + } + aSelCon->WordMove(isStep2Forward, PR_TRUE); // Select previous/next word + GetCurrentOffset(aClosure, aDomSel, aEndOffset); + } + break; + case BOUNDARY_LINE_START: + if (aType != eGetAt) { + // XXX, don't support yet + return NS_ERROR_NOT_IMPLEMENTED; + } + aSelCon->IntraLineMove(PR_FALSE, PR_FALSE); // Move caret to the line start + GetCurrentOffset(aClosure, aDomSel, aStartOffset); + aSelCon->IntraLineMove(PR_TRUE, PR_TRUE); // Move caret to the line end and select the whole line + GetCurrentOffset(aClosure, aDomSel, aEndOffset); + break; + case BOUNDARY_WORD_END: + { + // please refer to atk implementation (atktext.c) + // for specification of ATK_TEXT_BOUNDARY_WORD_END when before/at/after offset + // XXX, need to follow exact definition of ATK_TEXT_BOUNDARY_WORD_END + + if (aType != eGetAt) { + // XXX, don't support yet + return NS_ERROR_NOT_IMPLEMENTED; + } + + // Example of current code: _AB_CD_E_ ("_" is space) + // offset return string startOffset endOffset + // 0 AB_ 1 4 + // 1 AB_ 1 4 + // 2 AB_ 1 4 + // 3 AB_ 1 4 + // 4 CD_ 4 7 + // 5 CD_ 4 7 + // 6 CD_ 4 7 + // 7 E_ 7 9 + // 8 E_ 7 9 + + PRUnichar offsetChar; + nsresult rv = GetCharacterAtOffset(aOffset, &offsetChar); + NS_ENSURE_SUCCESS(rv, rv); + PRBool isOffsetEmpty = offsetChar == ' ' || offsetChar == '\t' || offsetChar == '\n'; + + PRInt32 stepBackwardCount = 0; // Times of move backward to find the word(e.g. "AB_") start boundary + if (aOffset == 0) { + if (isOffsetEmpty) + aSelCon->WordMove(PR_TRUE, PR_FALSE); // Move caret to the first word start boundary + } + else { + PRUnichar prevChar; + GetCharacterAtOffset(aOffset - 1, &prevChar); + PRBool isPrevEmpty = prevChar == ' ' || prevChar == '\t' || prevChar == '\n'; + if (!isPrevEmpty) + stepBackwardCount = 1; + else if (isOffsetEmpty) + stepBackwardCount = 2; + else + stepBackwardCount = 0; + + PRInt32 step; + for (step = 0; step < stepBackwardCount; step++) + aSelCon->WordMove(PR_FALSE, PR_FALSE); // Move caret to current word start boundary + } + + GetCurrentOffset(aClosure, aDomSel, aStartOffset); + // Move twice to select a "word" + aSelCon->WordMove(PR_TRUE, PR_TRUE); + aSelCon->WordMove(PR_TRUE, PR_TRUE); + GetCurrentOffset(aClosure, aDomSel, aEndOffset); + } + break; + case BOUNDARY_LINE_END: + case BOUNDARY_SENTENCE_START: + case BOUNDARY_SENTENCE_END: + case BOUNDARY_ATTRIBUTE_RANGE: + return NS_ERROR_NOT_IMPLEMENTED; + default: + return NS_ERROR_INVALID_ARG; + } + + nsXPIDLString text; + // Get text from selection + nsresult rv = aDomSel->ToString(getter_Copies(text)); + aDomSel->RemoveAllRanges(); + // restore the original selection range + aDomSel->AddRange(oldRange); + NS_ENSURE_SUCCESS(rv, rv); + + aText = text; + + // Ensure aStartOffset <= aEndOffset + if (*aStartOffset > *aEndOffset) { + PRInt32 tmp = *aStartOffset; + *aStartOffset = *aEndOffset; + *aEndOffset = tmp; + } + + return NS_OK; +} + +nsresult nsAccessibleText::GetTextHelper(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType, + PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset, + nsISupports *aClosure, nsAString &aText) +{ + NS_ENSURE_TRUE((aOffset >= 0), NS_ERROR_INVALID_ARG); + + nsCOMPtr selCon; + nsCOMPtr domSel; + + nsresult rv = GetSelections(getter_AddRefs(selCon), getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + //backup old settings + PRInt16 displaySelection; + selCon->GetDisplaySelection(&displaySelection); + PRBool caretEnable; + selCon->GetCaretEnabled(&caretEnable); + + //turn off display and caret + selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); + selCon->SetCaretEnabled(PR_FALSE); + + //turn off nsCaretAccessible::NotifySelectionChanged + gSuppressedNotifySelectionChanged = PR_TRUE; + + PRInt32 caretOffset = -1; + if (NS_SUCCEEDED(GetCaretOffset(&caretOffset))) { + if (caretOffset != aOffset) + SetCaretOffset(aOffset); + } + + *aStartOffset = *aEndOffset = aOffset; + rv = GetTextHelperCore(aType, aBoundaryType, aOffset, aStartOffset, aEndOffset, selCon, domSel, aClosure, aText); + + //restore caret offset + if (caretOffset >= 0) { + SetCaretOffset(caretOffset); + } + + //turn on nsCaretAccessible::NotifySelectionChanged + gSuppressedNotifySelectionChanged = PR_FALSE; + + //restore old settings + selCon->SetDisplaySelection(displaySelection); + selCon->SetCaretEnabled(caretEnable); + + return rv; +} + +/** + * nsIAccessibleText impl. + */ +/* + * Gets the offset position of the caret (cursor). + */ +NS_IMETHODIMP nsAccessibleText::GetCaretOffset(PRInt32 *aCaretOffset) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr focusNode; + domSel->GetFocusNode(getter_AddRefs(focusNode)); + if (focusNode != mTextNode) + return NS_ERROR_FAILURE; + + return domSel->GetFocusOffset(aCaretOffset); +} + +/* + * Sets the caret (cursor) position to the specified offset. + */ +NS_IMETHODIMP nsAccessibleText::SetCaretOffset(PRInt32 aCaretOffset) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr range(do_CreateInstance(kRangeCID)); + NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY); + + range->SetStart(mTextNode, aCaretOffset); + range->SetEnd(mTextNode, aCaretOffset); + domSel->RemoveAllRanges(); + return domSel->AddRange(range); +} + +/* + * Gets the character count. + */ +NS_IMETHODIMP nsAccessibleText::GetCharacterCount(PRInt32 *aCharacterCount) +{ + nsCOMPtr textContent(do_QueryInterface(mTextNode)); + if (!textContent) + return NS_ERROR_FAILURE; + + *aCharacterCount = textContent->TextLength(); + + return NS_OK; +} + +/* + * Gets the number of selected regions. + */ +NS_IMETHODIMP nsAccessibleText::GetSelectionCount(PRInt32 *aSelectionCount) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool isSelectionCollapsed; + rv = domSel->GetIsCollapsed(&isSelectionCollapsed); + NS_ENSURE_SUCCESS(rv, rv); + + if (isSelectionCollapsed) + *aSelectionCount = 0; + + rv = domSel->GetRangeCount(aSelectionCount); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +/* + * Gets the specified text. + */ +NS_IMETHODIMP nsAccessibleText::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString &aText) +{ + nsAutoString text; + mTextNode->GetNodeValue(text); + if (aEndOffset == -1) // get all text from aStartOffset + aEndOffset = text.Length(); + aText = Substring(text, aStartOffset, aEndOffset - aStartOffset); + return NS_OK; +} + +NS_IMETHODIMP nsAccessibleText::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText); +} + +NS_IMETHODIMP nsAccessibleText::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText); +} + +NS_IMETHODIMP nsAccessibleText::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType, + PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText) +{ + return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText); +} + +/* + * Gets the specified text. + */ +NS_IMETHODIMP nsAccessibleText::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter) +{ + nsAutoString text; + nsresult rv = GetText(aOffset, aOffset + 1, text); + NS_ENSURE_SUCCESS(rv, rv); + *aCharacter = text.First(); + return NS_OK; +} + +NS_IMETHODIMP nsAccessibleText::GetAttributeRange(PRInt32 aOffset, PRInt32 *aRangeStartOffset, + PRInt32 *aRangeEndOffset, nsISupports **aAttribute) +{ + // will do better job later + *aRangeStartOffset = aOffset; + GetCharacterCount(aRangeEndOffset); + *aAttribute = nsnull; + return NS_OK; +} + +/* + * Given an offset, the x, y, width, and height values are filled appropriately. + */ +NS_IMETHODIMP nsAccessibleText::GetCharacterExtents(PRInt32 aOffset, + PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight, + nsAccessibleCoordType aCoordType) +{ + nsCOMPtr domDoc; + mTextNode->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc(do_QueryInterface(domDoc)); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + nsIPresShell *shell = doc->GetShellAt(0); + NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); + + nsPresContext *context = shell->GetPresContext(); + NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); + + nsCOMPtr content(do_QueryInterface(mTextNode)); + NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); + + nsIFrame *frame = shell->GetPrimaryFrameFor(content); + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); + + nsIntRect frameScreenRect = frame->GetScreenRectExternal(); + + nsCOMPtr rc; + shell->CreateRenderingContext(frame, getter_AddRefs(rc)); + NS_ENSURE_TRUE(rc, NS_ERROR_FAILURE); + + const nsStyleFont *font = frame->GetStyleFont(); + + const nsStyleVisibility *visibility = frame->GetStyleVisibility(); + + if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLangGroup))) { + return NS_ERROR_FAILURE; + } + + nsIFontMetrics *fm; + rc->GetFontMetrics(fm); + NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE); + + float t2p = context->TwipsToPixels(); + + PRUnichar ch; + if (NS_SUCCEEDED(GetCharacterAtOffset(aOffset, &ch))) { + //Getting width + nscoord tmpWidth; + if (NS_SUCCEEDED(rc->GetWidth(ch, tmpWidth))) { + *aWidth = NSTwipsToIntPixels(tmpWidth, t2p); + } + + //Getting height + nscoord tmpHeight; + if (NS_SUCCEEDED(fm->GetHeight(tmpHeight))) { + *aHeight = NSTwipsToIntPixels(tmpHeight, t2p); + } + } + else { + //GetCharacterAtOffset() will fail when there is no text + *aWidth = *aHeight = 0; + } + + //add the width of the string before current char + nsAutoString beforeString; + nscoord beforeWidth; + if (NS_SUCCEEDED(GetText(0, aOffset, beforeString)) && + NS_SUCCEEDED(rc->GetWidth(beforeString, beforeWidth))) { + frameScreenRect.x += NSTwipsToIntPixels(beforeWidth, t2p); + } + + PRInt32 screenX = 0, screenY = 0; + if (aCoordType == COORD_TYPE_WINDOW) { + //co-ord type = window + nsCOMPtr docView(do_QueryInterface(doc)); + NS_ENSURE_TRUE(docView, NS_ERROR_FAILURE); + + nsCOMPtr abstractView; + docView->GetDefaultView(getter_AddRefs(abstractView)); + NS_ENSURE_TRUE(abstractView, NS_ERROR_FAILURE); + + nsCOMPtr windowInter(do_QueryInterface(abstractView)); + NS_ENSURE_TRUE(windowInter, NS_ERROR_FAILURE); + + if (NS_FAILED(windowInter->GetScreenX(&screenX)) || + NS_FAILED(windowInter->GetScreenY(&screenY))) { + return NS_ERROR_FAILURE; + } + } + // else default: co-ord type = screen + + *aX = frameScreenRect.x - screenX; + *aY = frameScreenRect.y - screenY; + + return NS_OK; +} + +/* + * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to + * the screen or this widget's window depending on coords. + */ +NS_IMETHODIMP nsAccessibleText::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY, nsAccessibleCoordType aCoordType, PRInt32 *aOffset) +{ + // will do better job later + *aOffset = 0; + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* + * Gets the start and end offset of the specified selection. + */ +NS_IMETHODIMP nsAccessibleText::GetSelectionBounds(PRInt32 aSelectionNum, PRInt32 *aStartOffset, PRInt32 *aEndOffset) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 rangeCount; + domSel->GetRangeCount(&rangeCount); + if (aSelectionNum < 0 || aSelectionNum >= rangeCount) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr range; + domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range)); + range->GetStartOffset(aStartOffset); + range->GetEndOffset(aEndOffset); + return NS_OK; +} + +/* + * Changes the start and end offset of the specified selection. + */ +NS_IMETHODIMP nsAccessibleText::SetSelectionBounds(PRInt32 aSelectionNum, PRInt32 aStartOffset, PRInt32 aEndOffset) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 rangeCount; + domSel->GetRangeCount(&rangeCount); + if (aSelectionNum < 0 || aSelectionNum >= rangeCount) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr range; + domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range)); + + nsCOMPtr startParent; + nsCOMPtr endParent; + range->GetStartContainer(getter_AddRefs(startParent)); + range->GetEndContainer(getter_AddRefs(endParent)); + PRInt32 oldEndOffset; + range->GetEndOffset(&oldEndOffset); + // to avoid set start point after the current end point + if (aStartOffset < oldEndOffset) { + range->SetStart(startParent, aStartOffset); + range->SetEnd(endParent, aEndOffset); + } + else { + range->SetEnd(endParent, aEndOffset); + range->SetStart(startParent, aStartOffset); + } + return NS_OK; +} + +/* + * Adds a selection bounded by the specified offsets. + */ +NS_IMETHODIMP nsAccessibleText::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset) +{ + nsCOMPtr selCon; + nsCOMPtr domSel; + + if (NS_FAILED(GetSelections(getter_AddRefs(selCon), getter_AddRefs(domSel)))) + return NS_ERROR_FAILURE; + + nsCOMPtr range(do_CreateInstance(kRangeCID)); + if (! range) + return NS_ERROR_OUT_OF_MEMORY; + + range->SetStart(mTextNode, aStartOffset); + range->SetEnd(mTextNode, aEndOffset); + return domSel->AddRange(range); +} + +/* + * Removes the specified selection. + */ +NS_IMETHODIMP nsAccessibleText::RemoveSelection(PRInt32 aSelectionNum) +{ + nsCOMPtr domSel; + nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 rangeCount; + domSel->GetRangeCount(&rangeCount); + if (aSelectionNum < 0 || aSelectionNum >= rangeCount) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr range; + domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range)); + return domSel->RemoveRange(range); +} + +// -------------------------------------------------------- +// nsAccessibleEditableText Accessible +// -------------------------------------------------------- +/** + * nsAccessibleEditableText implements the nsIAccessibleText interface for editable text, such as HTML + * ,