bug 345759 - Spell as you type not accessible, r=aaronlev, r=Olli.Pettay, sr=neil

This commit is contained in:
Alexander Surkov 2008-07-17 14:06:24 +02:00
parent b8397c4e80
commit 92240ca1b2
20 changed files with 1060 additions and 230 deletions

View File

@ -44,8 +44,9 @@
typedef long nsAccessibleTextBoundary;
interface nsIAccessible;
interface nsIPersistentProperties;
[scriptable, uuid(caa4f543-070e-4705-8428-2e53575c41bb)]
[scriptable, uuid(0f4633b1-550c-4b50-8c04-0eb1005eef2f)]
interface nsIAccessibleText : nsISupports
{
// In parameters for character offsets:
@ -102,13 +103,24 @@ interface nsIAccessibleText : nsISupports
wchar getCharacterAtOffset (in long offset);
/**
* Get the accessible and start/end offsets around the given offset.
* This accessible get return the DOM node and layout frame
* with the uniform attributes for this range of text
* Get the accessible start/end offsets around the given offset,
* return the text attributes for this range of text.
*
* @param includeDefAttrs [in] points whether text attributes applied to
* the entire accessible should be included or not.
* @param offset [in] text offset
* @param rangeStartOffset [out] start offset of the range of text
* @param rangeEndOffset [out] end offset of the range of text
*/
nsIAccessible getAttributeRange (in long offset,
out long rangeStartOffset,
out long rangeEndOffset);
nsIPersistentProperties getTextAttributes(in boolean includeDefAttrs,
in long offset,
out long rangeStartOffset,
out long rangeEndOffset);
/**
* Return the text attributes that apply to the entire accessible.
*/
readonly attribute nsIPersistentProperties defaultTextAttributes;
/**
* Returns the bounding box of the specified position.
@ -223,11 +235,6 @@ interface nsIAccessibleText : nsISupports
(since not every text component will allow every operation):
setSelectionBounds, addSelection, removeSelection, setCaretOffset.
getRangeAttributes defined to return an nsISupports
interface instead of a pango specific data structure.
It may be that some other return type is more appropriate
for mozilla text attributes.
we assume that all text components support the idea of
a caret offset, whether visible or "virtual". If this
isn't the case, caretOffset can be made readonly and

View File

@ -786,13 +786,50 @@ getRoleCB(AtkObject *aAtkObj)
return aAtkObj->role;
}
AtkAttributeSet*
ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes)
{
if (!aAttributes)
return nsnull;
AtkAttributeSet *objAttributeSet = nsnull;
nsCOMPtr<nsISimpleEnumerator> propEnum;
nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
NS_ENSURE_SUCCESS(rv, nsnull);
PRBool hasMore;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> sup;
rv = propEnum->GetNext(getter_AddRefs(sup));
NS_ENSURE_SUCCESS(rv, objAttributeSet);
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
NS_ENSURE_TRUE(propElem, objAttributeSet);
nsCAutoString name;
rv = propElem->GetKey(name);
NS_ENSURE_SUCCESS(rv, objAttributeSet);
nsAutoString value;
rv = propElem->GetValue(value);
NS_ENSURE_SUCCESS(rv, objAttributeSet);
AtkAttribute *objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
objAttr->name = g_strdup(name.get());
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
}
//libspi will free it
return objAttributeSet;
}
AtkAttributeSet *
GetAttributeSet(nsIAccessible* aAccessible)
{
AtkAttributeSet *objAttributeSet = nsnull;
nsCOMPtr<nsIPersistentProperties> attributes;
aAccessible->GetAttributes(getter_AddRefs(attributes));
if (attributes) {
// Deal with attributes that we only need to expose in ATK
PRUint32 state;
@ -804,33 +841,10 @@ GetAttributeSet(nsIAccessible* aAccessible)
oldValueUnused);
}
nsCOMPtr<nsISimpleEnumerator> propEnum;
nsresult rv = attributes->Enumerate(getter_AddRefs(propEnum));
NS_ENSURE_SUCCESS(rv, nsnull);
PRBool hasMore;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> sup;
rv = propEnum->GetNext(getter_AddRefs(sup));
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
NS_ENSURE_TRUE(propElem, nsnull);
nsCAutoString name;
rv = propElem->GetKey(name);
NS_ENSURE_SUCCESS(rv, nsnull);
nsAutoString value;
rv = propElem->GetValue(value);
NS_ENSURE_SUCCESS(rv, nsnull);
AtkAttribute *objAttribute = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
objAttribute->name = g_strdup(name.get());
objAttribute->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
objAttributeSet = g_slist_prepend(objAttributeSet, objAttribute);
}
return ConvertToAtkAttributeSet(attributes);
}
return objAttributeSet;
return nsnull;
}
AtkAttributeSet *
@ -1197,6 +1211,13 @@ nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
caretOffset);
} break;
case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_ATTRIBUTE_CHANGED\n"));
g_signal_emit_by_name(atkObj,
"text-attributes-changed");
break;
case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_MODEL_CHANGED\n"));
g_signal_emit_by_name(atkObj, "model_changed");

View File

@ -41,8 +41,9 @@
#include "nsMaiInterfaceText.h"
#include "nsString.h"
#include "nsIPersistentProperties2.h"
AtkAttributeSet * GetAttributeSet(nsIAccessible* aAccessible);
AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes);
void
textInterfaceInitCB(AtkTextIface *aIface)
@ -245,6 +246,9 @@ getRunAttributesCB(AtkText *aText, gint aOffset,
gint *aStartOffset,
gint *aEndOffset)
{
*aStartOffset = -1;
*aEndOffset = -1;
nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (!accWrap)
return nsnull;
@ -254,24 +258,37 @@ getRunAttributesCB(AtkText *aText, gint aOffset,
getter_AddRefs(accText));
NS_ENSURE_TRUE(accText, nsnull);
nsCOMPtr<nsIAccessible> accessibleWithAttrs;
nsCOMPtr<nsIPersistentProperties> attributes;
PRInt32 startOffset = 0, endOffset = 0;
nsresult rv =
accText->GetAttributeRange(aOffset, &startOffset, &endOffset,
getter_AddRefs(accessibleWithAttrs));
nsresult rv = accText->GetTextAttributes(PR_FALSE, aOffset,
&startOffset, &endOffset,
getter_AddRefs(attributes));
NS_ENSURE_SUCCESS(rv, nsnull);
*aStartOffset = startOffset;
*aEndOffset = endOffset;
if (NS_FAILED(rv))
return nsnull;
return GetAttributeSet(accessibleWithAttrs);
return ConvertToAtkAttributeSet(attributes);
}
AtkAttributeSet *
getDefaultAttributesCB(AtkText *aText)
{
/* not supported ??? */
return nsnull;
nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (!accWrap)
return nsnull;
nsCOMPtr<nsIAccessibleText> accText;
accWrap->QueryInterface(NS_GET_IID(nsIAccessibleText),
getter_AddRefs(accText));
NS_ENSURE_TRUE(accText, nsnull);
nsCOMPtr<nsIPersistentProperties> attributes;
nsresult rv = accText->GetDefaultTextAttributes(getter_AddRefs(attributes));
if (NS_FAILED(rv))
return nsnull;
return ConvertToAtkAttributeSet(attributes);
}
void

View File

@ -90,6 +90,7 @@ CPPSRCS = \
nsApplicationAccessible.cpp \
nsCaretAccessible.cpp \
nsTextAccessible.cpp \
nsTextUtils.cpp \
$(NULL)
EXPORTS = \

View File

@ -876,10 +876,7 @@ nsAccessNode::GetLanguage(nsAString& aLanguage)
}
}
nsIContent *walkUp = content;
while (walkUp && !walkUp->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::lang, aLanguage)) {
walkUp = walkUp->GetParent();
}
nsAccUtils::GetLanguageFor(content, nsnull, aLanguage);
if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
nsIDocument *doc = content->GetOwnerDoc();

View File

@ -45,6 +45,8 @@
#include "nsCOMPtr.h"
#include "nsAccessibilityAtoms.h"
#include "nsAccessibilityUtils.h"
#include "nsIAccessibleTypes.h"
#include "nsIAccessNode.h"
#include "nsIContent.h"

View File

@ -149,7 +149,7 @@ ACCESSIBILITY_ATOM(tooltip, "tooltip") // XUL
ACCESSIBILITY_ATOM(tr, "tr")
ACCESSIBILITY_ATOM(ul, "ul")
// Alphabetical list of attributes
// Alphabetical list of attributes (DOM)
ACCESSIBILITY_ATOM(acceltext, "acceltext")
ACCESSIBILITY_ATOM(accesskey, "accesskey")
ACCESSIBILITY_ATOM(alt, "alt")
@ -186,6 +186,10 @@ ACCESSIBILITY_ATOM(tooltiptext, "tooltiptext")
ACCESSIBILITY_ATOM(type, "type")
ACCESSIBILITY_ATOM(value, "value")
// Alphabetical list of text attributes (AT API)
ACCESSIBILITY_ATOM(invalid, "invalid")
ACCESSIBILITY_ATOM(language, "language")
// ARIA (DHTML accessibility) attributes
// Also add to nsARIAMap.cpp and nsARIAMap.h
// ARIA role attribute

View File

@ -340,6 +340,24 @@ nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible,
return pAccessible->FireAccessibleEvent(event);
}
already_AddRefed<nsIDOMElement>
nsAccUtils::GetDOMElementFor(nsIDOMNode *aNode)
{
nsCOMPtr<nsINode> node(do_QueryInterface(aNode));
nsIDOMElement *element = nsnull;
if (node->IsNodeOfType(nsINode::eELEMENT))
CallQueryInterface(node, &element);
else if (node->IsNodeOfType(nsINode::eTEXT))
CallQueryInterface(node->GetNodeParent(), &element);
else if (node->IsNodeOfType(nsINode::eDOCUMENT)) {
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(node));
domDoc->GetDocumentElement(&element);
}
return element;
}
PRBool
nsAccUtils::IsAncestorOf(nsIDOMNode *aPossibleAncestorNode,
nsIDOMNode *aPossibleDescendantNode)
@ -921,6 +939,19 @@ nsAccUtils::FindDescendantPointingToIDImpl(nsCString& aIdWithSpaces,
return nsnull;
}
void
nsAccUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
nsAString& aLanguage)
{
aLanguage.Truncate();
nsIContent *walkUp = aContent;
while (walkUp && walkUp != aRootContent &&
!walkUp->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::lang, aLanguage))
walkUp = walkUp->GetParent();
}
nsRoleMapEntry*
nsAccUtils::GetRoleMapEntry(nsIDOMNode *aNode)
{

View File

@ -138,6 +138,16 @@ public:
static nsresult FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible,
PRBool aIsAsynch = PR_FALSE);
/**
* Return DOM element related with the given node, i.e.
* a) itself if it is DOM element
* b) parent element if it is text node
* c) document element if it is document node.
*
* @param aNode [in] the given DOM node
*/
static already_AddRefed<nsIDOMElement> GetDOMElementFor(nsIDOMNode *aNode);
/**
* Is the first passed in node an ancestor of the second?
* Note: A node is not considered to be the ancestor of itself.
@ -302,6 +312,16 @@ public:
*/
static PRBool IsXLink(nsIContent *aContent);
/**
* Returns language for the given node.
*
* @param aContent [in] the given node
* @param aRootContent [in] container of the given node
* @param aLanguage [out] language
*/
static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
nsAString& aLanguage);
/**
* Get the role map entry for a given DOM node. This will use the first
* ARIA role if the role attribute provides a space delimited list of roles.

View File

@ -40,7 +40,6 @@
#define _nsAccessible_H_
#include "nsAccessNodeWrap.h"
#include "nsAccessibilityUtils.h"
#include "nsIAccessible.h"
#include "nsPIAccessible.h"

View File

@ -47,8 +47,8 @@
#include "nsIFrame.h"
#include "nsIPresShell.h"
#include "nsRootAccessible.h"
#include "nsISelectionController.h"
#include "nsISelectionPrivate.h"
#include "nsISelection2.h"
#include "nsServiceManagerUtils.h"
#include "nsIViewManager.h"
#include "nsIWidget.h"
@ -78,13 +78,31 @@ void nsCaretAccessible::Shutdown()
nsresult nsCaretAccessible::ClearControlSelectionListener()
{
mCurrentControl = nsnull;
mCurrentControlSelection = nsnull;
nsCOMPtr<nsISelectionController> controller =
GetSelectionControllerForNode(mCurrentControl);
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryReferent(mCurrentControlSelection));
if (!selPrivate) {
mCurrentControl = nsnull;
if (!controller)
return NS_OK;
}
// Remove 'this' registered as selection listener for the normal selection.
nsCOMPtr<nsISelection> normalSel;
controller->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(normalSel));
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(normalSel));
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
nsresult rv = selPrivate->RemoveSelectionListener(this);
NS_ENSURE_SUCCESS(rv, rv);
// Remove 'this' registered as selection listener for the spellcheck
// selection.
nsCOMPtr<nsISelection> spellcheckSel;
controller->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
getter_AddRefs(spellcheckSel));
selPrivate = do_QueryInterface(spellcheckSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
return selPrivate->RemoveSelectionListener(this);
}
@ -99,39 +117,29 @@ nsresult nsCaretAccessible::SetControlSelectionListener(nsIDOMNode *aCurrentNode
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 =
mRootAccessible->GetPresShellFor(aCurrentNode);
if (!presShell)
return NS_ERROR_FAILURE;
// this removes the old selection listener and attaches a new one for
// the current focus.
nsCOMPtr<nsISelectionController> controller =
GetSelectionControllerForNode(mCurrentControl);
NS_ENSURE_TRUE(controller, NS_ERROR_FAILURE);
nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
nsCOMPtr<nsIContent> content(do_QueryInterface(aCurrentNode));
// The control selection listener is only for form controls, not for the document
// When there is no document, the content will be null
if (!content) {
return NS_OK;
}
nsIFrame *frame = presShell->GetPrimaryFrameFor(content);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
nsPresContext *presContext = presShell->GetPresContext();
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
nsCOMPtr<nsISelectionController> selCon;
frame->GetSelectionController(presContext, getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
nsCOMPtr<nsISelection> domSel;
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(domSel));
// Register 'this' as selection listener for the normal selection.
nsCOMPtr<nsISelection> normalSel;
controller->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(normalSel));
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(normalSel));
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
mCurrentControlSelection = do_GetWeakReference(domSel);
nsresult rv = selPrivate->AddSelectionListener(this);
NS_ENSURE_SUCCESS(rv, rv);
// Register 'this' as selection listener for the spellcheck selection.
nsCOMPtr<nsISelection> spellcheckSel;
controller->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
getter_AddRefs(spellcheckSel));
selPrivate = do_QueryInterface(spellcheckSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
return selPrivate->AddSelectionListener(this);
}
@ -148,6 +156,15 @@ nsCaretAccessible::AddDocSelectionListener(nsIPresShell *aShell)
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(domSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
nsresult rv = selPrivate->AddSelectionListener(this);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> spellcheckSel;
selCon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
getter_AddRefs(spellcheckSel));
selPrivate = do_QueryInterface(spellcheckSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
return selPrivate->AddSelectionListener(this);
}
@ -162,10 +179,39 @@ nsCaretAccessible::RemoveDocSelectionListener(nsIPresShell *aShell)
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(domSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
selPrivate->RemoveSelectionListener(this);
nsCOMPtr<nsISelection> spellcheckSel;
selCon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
getter_AddRefs(spellcheckSel));
selPrivate = do_QueryInterface(spellcheckSel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
return selPrivate->RemoveSelectionListener(this);
}
NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel, PRInt16 aReason)
NS_IMETHODIMP
nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc,
nsISelection *aSel,
PRInt16 aReason)
{
nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSel));
PRInt16 type = 0;
sel2->GetType(&type);
if (type == nsISelectionController::SELECTION_NORMAL)
return NormalSelectionChanged(aDoc, aSel);
if (type == nsISelectionController::SELECTION_SPELLCHECK)
return SpellcheckSelectionChanged(aDoc, aSel);
return NS_OK;
}
nsresult
nsCaretAccessible::NormalSelectionChanged(nsIDOMDocument *aDoc,
nsISelection *aSel)
{
NS_ENSURE_TRUE(mRootAccessible, NS_ERROR_FAILURE);
@ -247,6 +293,38 @@ NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, ns
return mRootAccessible->FireDelayedAccessibleEvent(event);
}
nsresult
nsCaretAccessible::SpellcheckSelectionChanged(nsIDOMDocument *aDoc,
nsISelection *aSel)
{
// XXX: fire an event for accessible of focus node of the selection. If
// spellchecking is enabled then we will fire the number of events for
// the same accessible for newly appended range of the selection (for every
// misspelled word). If spellchecking is disabled (for example,
// @spellcheck="false" on html:body) then we won't fire any event.
nsCOMPtr<nsIDOMNode> targetNode;
aSel->GetFocusNode(getter_AddRefs(targetNode));
if (!targetNode)
return NS_OK;
nsCOMPtr<nsIAccessibleDocument> docAccessible =
nsAccessNode::GetDocAccessibleFor(targetNode);
NS_ENSURE_STATE(docAccessible);
nsCOMPtr<nsIAccessible> containerAccessible;
nsresult rv =
docAccessible->GetAccessibleInParentChain(targetNode, PR_TRUE,
getter_AddRefs(containerAccessible));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAccessibleEvent> event =
new nsAccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
containerAccessible, nsnull);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return mRootAccessible->FireAccessibleEvent(event);
}
nsRect
nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget)
{
@ -317,3 +395,35 @@ nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget)
return caretRect;
}
already_AddRefed<nsISelectionController>
nsCaretAccessible::GetSelectionControllerForNode(nsIDOMNode *aNode)
{
if (!aNode)
return nsnull;
nsCOMPtr<nsIPresShell> presShell = mRootAccessible->GetPresShellFor(aNode);
if (!presShell)
return nsnull;
nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
if (!doc)
return nsnull;
// Get selection controller only for form controls, not for the document.
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
if (!content)
return nsnull;
nsIFrame *frame = presShell->GetPrimaryFrameFor(content);
if (!frame)
return nsnull;
nsPresContext *presContext = presShell->GetPresContext();
if (!presContext)
return nsnull;
nsISelectionController *controller = nsnull;
frame->GetSelectionController(presContext, &controller);
return controller;
}

View File

@ -42,6 +42,7 @@
#include "nsIAccessibleText.h"
#include "nsIDOMNode.h"
#include "nsISelectionListener.h"
#include "nsISelectionController.h"
#include "nsRect.h"
class nsRootAccessible;
@ -120,17 +121,23 @@ public:
nsRect GetCaretRect(nsIWidget **aOutWidget);
protected:
nsresult NormalSelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel);
nsresult SpellcheckSelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel);
already_AddRefed<nsISelectionController>
GetSelectionControllerForNode(nsIDOMNode *aNode);
private:
// The currently focused control -- never a document.
// We listen to selection for one control at a time (the focused one)
// Document selection is handled separately via additional listeners on all active documents
// The current control is set via SetControlSelectionListener()
nsCOMPtr<nsIDOMNode> mCurrentControl; // Selection controller for the currently focused control
nsCOMPtr<nsIWeakReference> mCurrentControlSelection;
// Info for the the last selection event
// If it was on a control, then mLastUsedSelection == mCurrentControlSelection
// Otherwise, it's for a document where the selection changed
// Info for the the last selection event.
// If it was on a control, then it's control's selection. Otherwise, it's for
// a document where the selection changed.
nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection
nsCOMPtr<nsIAccessibleText> mLastTextAccessible;
PRInt32 mLastCaretOffset;

View File

@ -41,6 +41,8 @@
#include "nsAccessibilityAtoms.h"
#include "nsAccessibilityService.h"
#include "nsAccessibleTreeWalker.h"
#include "nsTextUtils.h"
#include "nsPIAccessNode.h"
#include "nsIClipboard.h"
#include "nsContentCID.h"
@ -50,6 +52,7 @@
#include "nsPIDOMWindow.h"
#include "nsIDOMDocumentView.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsIDOMWindowInternal.h"
#include "nsIDOMXULDocument.h"
#include "nsIEditingSession.h"
@ -671,7 +674,7 @@ nsresult nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode* aNode, PRI
// Start offset, inclusive
// Make sure the offset lands on the embedded object character in order to indicate
// the true inner offset is inside the subtree for that link
addTextOffset = (TextLength(descendantAccessible) == addTextOffset) ? 1 : 0;
addTextOffset = (TextLength(descendantAccessible) == static_cast<PRInt32>(addTextOffset)) ? 1 : 0;
}
descendantAccessible = parentAccessible;
}
@ -698,6 +701,18 @@ nsresult nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode* aNode, PRI
return NS_OK;
}
nsresult
nsHyperTextAccessible::HypertextOffsetToDOMPoint(PRInt32 aHTOffset,
nsIDOMNode **aNode,
PRInt32 *aOffset)
{
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
return HypertextOffsetsToDOMRange(aHTOffset, aHTOffset, aNode, aOffset,
getter_AddRefs(endNode), &endOffset);
}
nsresult
nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
PRInt32 aEndHTOffset,
@ -888,11 +903,15 @@ nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTe
// otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
// at the end of a line.
nsCOMPtr<nsISelection> domSel;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSel));
nsCOMPtr<nsFrameSelection> frameSelection;
rv = privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
NS_ENSURE_SUCCESS(rv, rv);
if (frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
-- aOffset; // We are at the start of a line
}
@ -1071,31 +1090,103 @@ NS_IMETHODIMP nsHyperTextAccessible::GetTextAfterOffset(PRInt32 aOffset, nsAcces
return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
}
NS_IMETHODIMP nsHyperTextAccessible::GetAttributeRange(PRInt32 aOffset, PRInt32 *aRangeStartOffset,
PRInt32 *aRangeEndOffset, nsIAccessible **aAccessibleWithAttrs)
// nsIPersistentProperties
// nsIAccessibleText::getTextAttributes(in boolean includeDefAttrs,
// in long offset,
// out long rangeStartOffset,
// out long rangeEndOffset);
NS_IMETHODIMP
nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs,
PRInt32 aOffset,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties **aAttributes)
{
// Return the range of text with common attributes around aOffset
*aRangeStartOffset = *aRangeEndOffset = 0;
*aAccessibleWithAttrs = nsnull;
// 1. First we get spell check, then language, then the set of CSS-based
// attributes.
// 2. As we get each new attribute, we pass the current start and end offsets
// as in/out parameters. In other words, as attributes are collected,
// the attribute range itself can only stay the same or get smaller.
//
// Example:
// Current: range 5-10
// Adding: range 7-12
// Result: range 7-10
if (!mDOMNode) {
NS_ENSURE_ARG_POINTER(aStartOffset);
*aStartOffset = 0;
NS_ENSURE_ARG_POINTER(aEndOffset);
nsresult rv = GetCharacterCount(aEndOffset);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG_POINTER(aAttributes);
*aAttributes = nsnull;
nsCOMPtr<nsIPersistentProperties> attributes =
do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aAttributes = attributes);
if (!mDOMNode)
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIAccessible> accessible;
while (NextChild(accessible)) {
PRInt32 length = TextLength(accessible);
NS_ENSURE_TRUE(length >= 0, NS_ERROR_FAILURE);
if (*aRangeStartOffset + length > aOffset) {
*aRangeEndOffset = *aRangeStartOffset + length;
NS_ADDREF(*aAccessibleWithAttrs = accessible);
return NS_OK;
}
*aRangeStartOffset += length;
}
nsCOMPtr<nsIDOMNode> node;
PRInt32 nodeOffset = 0;
rv = HypertextOffsetToDOMPoint(aOffset, getter_AddRefs(node), &nodeOffset);
NS_ENSURE_SUCCESS(rv, rv);
return NS_ERROR_FAILURE;
// Set 'misspelled' text attribute.
rv = GetSpellTextAttribute(node, nodeOffset, aStartOffset, aEndOffset,
*aAttributes);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
if (content && content->IsNodeOfType(nsINode::eELEMENT))
node = do_QueryInterface(content->GetChildAt(nodeOffset));
if (!node)
return NS_OK;
// Set 'lang' text attribute.
rv = GetLangTextAttributes(aIncludeDefAttrs, node,
aStartOffset, aEndOffset, *aAttributes);
NS_ENSURE_SUCCESS(rv, rv);
// Set CSS based text attributes.
rv = GetCSSTextAttributes(aIncludeDefAttrs, node,
aStartOffset, aEndOffset, *aAttributes);
return rv;
}
// nsIPersistentProperties
// nsIAccessibleText::defaultTextAttributes
NS_IMETHODIMP
nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties **aAttributes)
{
NS_ENSURE_ARG_POINTER(aAttributes);
*aAttributes = nsnull;
nsCOMPtr<nsIPersistentProperties> attributes =
do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aAttributes = attributes);
if (!mDOMNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMElement> element = nsAccUtils::GetDOMElementFor(mDOMNode);
nsCSSTextAttr textAttr(PR_TRUE, element, nsnull);
while (textAttr.iterate()) {
nsCAutoString name;
nsAutoString value, oldValue;
if (textAttr.get(name, value))
attributes->SetStringProperty(name, value, oldValue);
}
return NS_OK;
}
nsresult
@ -1474,7 +1565,8 @@ nsresult nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEn
// ranges remaining from previous selection
nsCOMPtr<nsISelection> domSel;
nsCOMPtr<nsISelectionController> selCon;
GetSelections(getter_AddRefs(selCon), getter_AddRefs(domSel));
GetSelections(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selCon), getter_AddRefs(domSel));
if (domSel) {
PRInt32 numRanges;
domSel->GetRangeCount(&numRanges);
@ -1509,7 +1601,8 @@ NS_IMETHODIMP nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
*aCaretOffset = 0;
nsCOMPtr<nsISelection> domSel;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> caretNode;
@ -1527,7 +1620,8 @@ PRInt32 nsHyperTextAccessible::GetCaretLineNumber()
// Provide the line number for the caret, relative to the
// currently focused node. Use a 1-based index
nsCOMPtr<nsISelection> domSel;
GetSelections(nsnull, getter_AddRefs(domSel));
GetSelections(nsISelectionController::SELECTION_NORMAL, nsnull,
getter_AddRefs(domSel));
nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSel));
NS_ENSURE_TRUE(privateSelection, -1);
nsCOMPtr<nsFrameSelection> frameSelection;
@ -1590,9 +1684,11 @@ PRInt32 nsHyperTextAccessible::GetCaretLineNumber()
return lineNumber;
}
nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon,
nsISelection **aDomSel,
nsCOMArray<nsIDOMRange>* aRanges)
nsresult
nsHyperTextAccessible::GetSelections(PRInt16 aType,
nsISelectionController **aSelCon,
nsISelection **aDomSel,
nsCOMArray<nsIDOMRange>* aRanges)
{
if (!mDOMNode) {
return NS_ERROR_FAILURE;
@ -1610,7 +1706,6 @@ nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon,
nsCOMPtr<nsISelection> domSel;
nsCOMPtr<nsISelectionController> selCon;
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIEditor> editor;
GetAssociatedEditor(getter_AddRefs(editor));
nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
@ -1619,18 +1714,7 @@ nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon,
// This is for form controls which have their own
// selection controller separate from the document, for example
// HTML:input, HTML:textarea, XUL:textbox, etc.
if (aSelCon) {
editor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(*aSelCon, NS_ERROR_FAILURE);
}
editor->GetSelection(getter_AddRefs(domSel));
NS_ENSURE_TRUE(domSel, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMElement> editorRoot;
editor->GetRootElement(getter_AddRefs(editorRoot));
startNode = do_QueryInterface(editorRoot);
NS_ENSURE_STATE(startNode);
editor->GetSelectionController(getter_AddRefs(selCon));
}
else {
// Case 2: rich content subtree (can be rich editor)
@ -1641,13 +1725,11 @@ nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon,
// Get the selection and selection controller
frame->GetSelectionController(GetPresContext(),
getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(domSel));
NS_ENSURE_TRUE(domSel, NS_ERROR_FAILURE);
startNode = mDOMNode;
}
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
selCon->GetSelection(aType, getter_AddRefs(domSel));
NS_ENSURE_TRUE(domSel, NS_ERROR_FAILURE);
if (aSelCon) {
NS_ADDREF(*aSelCon = selCon);
@ -1655,10 +1737,19 @@ nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon,
if (aDomSel) {
NS_ADDREF(*aDomSel = domSel);
}
if (aRanges) {
nsCOMPtr<nsISelection2> selection2(do_QueryInterface(domSel));
NS_ENSURE_TRUE(selection2, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMNode> startNode(mDOMNode);
if (peditor) {
nsCOMPtr<nsIDOMElement> editorRoot;
editor->GetRootElement(getter_AddRefs(editorRoot));
startNode = do_QueryInterface(editorRoot);
}
NS_ENSURE_STATE(startNode);
nsCOMPtr<nsIDOMNodeList> childNodes;
nsresult rv = startNode->GetChildNodes(getter_AddRefs(childNodes));
NS_ENSURE_SUCCESS(rv, rv);
@ -1692,7 +1783,8 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
{
nsCOMPtr<nsISelection> domSel;
nsCOMArray<nsIDOMRange> ranges;
nsresult rv = GetSelections(nsnull, nsnull, &ranges);
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, nsnull, &ranges);
NS_ENSURE_SUCCESS(rv, rv);
*aSelectionCount = ranges.Count();
@ -1709,7 +1801,8 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, P
nsCOMPtr<nsISelection> domSel;
nsCOMArray<nsIDOMRange> ranges;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel), &ranges);
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel), &ranges);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 rangeCount = ranges.Count();
@ -1762,7 +1855,8 @@ nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
PRInt32 aEndOffset)
{
nsCOMPtr<nsISelection> domSel;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel));
NS_ENSURE_SUCCESS(rv, rv);
// Caret is a collapsed selection
@ -1810,7 +1904,8 @@ nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
NS_IMETHODIMP nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
{
nsCOMPtr<nsISelection> domSel;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 rangeCount;
@ -1825,7 +1920,8 @@ NS_IMETHODIMP nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32
NS_IMETHODIMP nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
{
nsCOMPtr<nsISelection> domSel;
nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
nsnull, getter_AddRefs(domSel));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 rangeCount;
@ -2037,3 +2133,352 @@ nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame *aFrame,
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::DOMRangeBoundToHypertextOffset(nsIDOMRange *aRange,
PRBool aIsStartBound,
PRBool aIsStartHTOffset,
PRInt32 *aHTOffset)
{
nsCOMPtr<nsIDOMNode> node;
PRInt32 nodeOffset = 0;
nsresult rv;
if (aIsStartBound) {
rv = aRange->GetStartContainer(getter_AddRefs(node));
NS_ENSURE_SUCCESS(rv, rv);
rv = aRange->GetStartOffset(&nodeOffset);
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = aRange->GetEndContainer(getter_AddRefs(node));
NS_ENSURE_SUCCESS(rv, rv);
rv = aRange->GetEndOffset(&nodeOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIAccessible> startAcc;
rv = DOMPointToHypertextOffset(node, nodeOffset, aHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (aIsStartHTOffset && !startAcc)
*aHTOffset = 0;
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
PRInt32 aNodeOffset,
PRInt32 *aHTStartOffset,
PRInt32 *aHTEndOffset,
nsIPersistentProperties *aAttributes)
{
nsCOMArray<nsIDOMRange> ranges;
nsresult rv = GetSelections(nsISelectionController::SELECTION_SPELLCHECK,
nsnull, nsnull, &ranges);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 rangeCount = ranges.Count();
if (!rangeCount)
return NS_OK;
for (PRInt32 index = 0; index < rangeCount; index++) {
nsCOMPtr<nsIDOMRange> range = ranges[index];
nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
NS_ENSURE_STATE(nsrange);
PRInt16 result;
rv = nsrange->ComparePoint(aNode, aNodeOffset, &result);
NS_ENSURE_SUCCESS(rv, rv);
if (result == 1) { // range is before point
PRInt32 startHTOffset = 0;
rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_TRUE,
&startHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (startHTOffset > *aHTStartOffset)
*aHTStartOffset = startHTOffset;
} else if (result == -1) { // range is after point
PRInt32 endHTOffset = 0;
rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_FALSE,
&endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aHTEndOffset)
*aHTEndOffset = endHTOffset;
} else { // point is in range
PRInt32 startHTOffset = 0;
rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_TRUE,
&startHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 endHTOffset = 0;
rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_FALSE,
&endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (startHTOffset > *aHTStartOffset)
*aHTStartOffset = startHTOffset;
if (endHTOffset < *aHTEndOffset)
*aHTEndOffset = endHTOffset;
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::invalid,
NS_LITERAL_STRING("spelling"));
return NS_OK;
}
}
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetLangTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset,
nsIPersistentProperties *aAttributes)
{
nsCOMPtr<nsIDOMElement> sourceElm(nsAccUtils::GetDOMElementFor(aSourceNode));
nsCOMPtr<nsIContent> content(do_QueryInterface(sourceElm));
nsCOMPtr<nsIContent> rootContent(do_QueryInterface(mDOMNode));
nsAutoString lang;
nsAccUtils::GetLanguageFor(content, rootContent, lang);
nsAutoString rootLang;
nsresult rv = GetLanguage(rootLang);
NS_ENSURE_SUCCESS(rv, rv);
// Expose 'language' text attribute if the DOM 'lang' attribute is
// presented and it's different from the 'lang' attribute on the root
// element or we should include default values of text attribute.
const nsAString& resultLang = lang.IsEmpty() ? rootLang : lang;
if (!resultLang.IsEmpty() && (aIncludeDefAttrs || lang != rootLang))
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::language,
resultLang);
nsLangTextAttr textAttr(lang, rootContent);
return GetRangeForTextAttr(aSourceNode, &textAttr,
aStartHTOffset, aEndHTOffset);
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetCSSTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset,
nsIPersistentProperties *aAttributes)
{
nsCOMPtr<nsIDOMElement> sourceElm(nsAccUtils::GetDOMElementFor(aSourceNode));
nsCOMPtr<nsIDOMElement> rootElm(nsAccUtils::GetDOMElementFor(mDOMNode));
nsCSSTextAttr textAttr(aIncludeDefAttrs, sourceElm, rootElm);
while (textAttr.iterate()) {
nsCAutoString name;
nsAutoString value, oldValue;
if (textAttr.get(name, value))
aAttributes->SetStringProperty(name, value, oldValue);
nsresult rv = GetRangeForTextAttr(aSourceNode, &textAttr,
aStartHTOffset, aEndHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// nsHyperTextAccessible
nsresult
nsHyperTextAccessible::GetRangeForTextAttr(nsIDOMNode *aNode,
nsTextAttr *aComparer,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset)
{
nsCOMPtr<nsIDOMElement> rootElm(nsAccUtils::GetDOMElementFor(mDOMNode));
NS_ENSURE_STATE(rootElm);
nsCOMPtr<nsIDOMNode> tmpNode(aNode);
nsCOMPtr<nsIDOMNode> currNode(aNode);
// Navigate backwards and forwards from current node to the root node to
// calculate range bounds for the text attribute. Navigation sequence is the
// following:
// 1. Navigate through the siblings.
// 2. If the traversed sibling has children then navigate from its leaf child
// to it through whole tree of the traversed sibling.
// 3. Get the parent and cycle algorithm until the root node.
// Navigate backwards (find the start offset).
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsAccUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
if (currNode != aNode && !aComparer->equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = DOMPointToHypertextOffset(tmpNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aStartHTOffset)
*aStartHTOffset = startHTOffset;
break;
}
currNode->GetPreviousSibling(getter_AddRefs(tmpNode));
if (tmpNode) {
// Navigate through the subtree of traversed children to calculate
// left bound of the range.
FindStartOffsetInSubtree(tmpNode, currNode, aComparer, aStartHTOffset);
}
currNode->GetParentNode(getter_AddRefs(tmpNode));
currNode.swap(tmpNode);
}
// Navigate forwards (find the end offset).
PRBool moveIntoSubtree = PR_TRUE;
currNode = aNode;
while (currNode && currNode != rootElm) {
nsCOMPtr<nsIDOMElement> currElm(nsAccUtils::GetDOMElementFor(currNode));
NS_ENSURE_STATE(currElm);
// Stop new end offset searching if the given text attribute changes its
// value.
if (!aComparer->equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = DOMPointToHypertextOffset(currNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aEndHTOffset)
*aEndHTOffset = endHTOffset;
break;
}
if (moveIntoSubtree) {
// Navigate through subtree of traversed node. We use 'moveIntoSubtree'
// flag to avoid traversing the same subtree twice.
currNode->GetFirstChild(getter_AddRefs(tmpNode));
if (tmpNode)
FindEndOffsetInSubtree(tmpNode, aComparer, aEndHTOffset);
}
currNode->GetNextSibling(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_TRUE;
if (!tmpNode) {
currNode->GetParentNode(getter_AddRefs(tmpNode));
moveIntoSubtree = PR_FALSE;
}
currNode.swap(tmpNode);
}
return NS_OK;
}
PRBool
nsHyperTextAccessible::FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
nsCOMPtr<nsIDOMElement> currElm(nsAccUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the end of range.
if (!aComparer->equal(currElm)) {
PRInt32 endHTOffset = 0;
nsresult rv = DOMPointToHypertextOffset(aCurrNode, -1, &endHTOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (endHTOffset < *aHTOffset)
*aHTOffset = endHTOffset;
return PR_TRUE;
}
// Deeply traverse into the tree to fit the end of range.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetFirstChild(getter_AddRefs(nextNode));
if (nextNode) {
PRBool res = FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset);
if (res)
return res;
}
aCurrNode->GetNextSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsHyperTextAccessible::FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
nsIDOMNode *aPrevNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset)
{
if (!aCurrNode)
return PR_FALSE;
// Find the closest element back to the traversed element.
nsCOMPtr<nsIDOMNode> nextNode;
aCurrNode->GetLastChild(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(nextNode, aPrevNode, aComparer, aHTOffset))
return PR_TRUE;
}
nsCOMPtr<nsIDOMElement> currElm(nsAccUtils::GetDOMElementFor(aCurrNode));
NS_ENSURE_STATE(currElm);
// If the given text attribute (pointed by nsTextAttr object) changes its
// value on the traversed element then fit the start of range.
if (!aComparer->equal(currElm)) {
PRInt32 startHTOffset = 0;
nsCOMPtr<nsIAccessible> startAcc;
nsresult rv = DOMPointToHypertextOffset(aPrevNode, -1, &startHTOffset,
getter_AddRefs(startAcc));
NS_ENSURE_SUCCESS(rv, rv);
if (!startAcc)
startHTOffset = 0;
if (startHTOffset > *aHTOffset)
*aHTOffset = startHTOffset;
return PR_TRUE;
}
// Moving backwards to find the start of range.
aCurrNode->GetPreviousSibling(getter_AddRefs(nextNode));
if (nextNode) {
if (FindStartOffsetInSubtree(nextNode, aCurrNode, aComparer, aHTOffset))
return PR_TRUE;
}
return PR_FALSE;
}

View File

@ -45,6 +45,8 @@
#include "nsIAccessibleHyperText.h"
#include "nsIAccessibleEditableText.h"
#include "nsAccessibleEventData.h"
#include "nsTextUtils.h"
#include "nsFrameSelection.h"
#include "nsISelectionController.h"
@ -64,7 +66,6 @@ const PRUnichar kForcedNewLineChar = '\n';
{ 0xa9, 0x2e, 0x95, 0x23, 0x97, 0x05, 0xf3, 0x0b } \
}
/**
* Special Accessible that knows how contain both text and embedded objects
*/
@ -81,8 +82,11 @@ public:
NS_DECL_NSIACCESSIBLEEDITABLETEXT
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HYPERTEXTACCESSIBLE_IMPL_CID)
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
// nsAccessible
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
void CacheChildren();
@ -124,6 +128,17 @@ public:
nsIAccessible **aFinalAccessible = nsnull,
PRBool aIsEndOffset = PR_FALSE);
/**
* Turn a hypertext offsets into DOM point.
*
* @param aHTOffset [in] the given start hypertext offset
* @param aNode [out] start node
* @param aOffset [out] offset inside the start node
*/
nsresult HypertextOffsetToDOMPoint(PRInt32 aHTOffset,
nsIDOMNode **aNode,
PRInt32 *aOffset);
/**
* Turn a start and end hypertext offsets into DOM range.
*
@ -211,15 +226,23 @@ protected:
// Selection helpers
/**
* Get the relevant selection interfaces and ranges for the current hyper text
* @param aSelCon The selection controller for the current hyper text, or nsnull if not needed
* @param aDomSel The selection interface for the current hyper text, or nsnull if not needed
* @param aRanges The selected ranges within the current subtree, or nsnull if not needed
/**
* Get the relevant selection interfaces and ranges for the current hyper
* text.
*
* @param aType [in] the selection type
* @param aSelCon [out, optional] the selection controller for the current
* hyper text
* @param aDomSel [out, optional] the selection interface for the current
* hyper text
* @param aRanges [out, optional] the selected ranges within the current
* subtree
*/
nsresult GetSelections(nsISelectionController **aSelCon,
nsresult GetSelections(PRInt16 aType,
nsISelectionController **aSelCon,
nsISelection **aDomSel = nsnull,
nsCOMArray<nsIDOMRange>* aRanges = nsnull);
nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
/**
@ -233,6 +256,125 @@ protected:
nsresult GetDOMPointByFrameOffset(nsIFrame *aFrame, PRInt32 aOffset,
nsIAccessible *aAccessible,
nsIDOMNode **aNode, PRInt32 *aNodeOffset);
/**
* Return hyper text offset for the specified bound of the given DOM range.
* If the bound is outside of the hyper text then offset value is either
* 0 or number of characters of hyper text, it depends on type of requested
* offset. The method is a wrapper for DOMPointToHypertextOffset.
*
* @param aRange [in] the given range
* @param aIsStartBound [in] specifies whether the required range bound is
* start bound
* @param aIsStartOffset [in] the offset type, used when the range bound is
* outside of hyper text
* @param aHTOffset [out] the result offset
*/
nsresult DOMRangeBoundToHypertextOffset(nsIDOMRange *aRange,
PRBool aIsStartBound,
PRBool aIsStartOffset,
PRInt32 *aHTOffset);
/**
* Set 'misspelled' text attribute and return range offsets where the
* attibute is stretched. If the text is not misspelled at the given offset
* then we expose only range offsets where text is not misspelled. The method
* is used by GetTextAttributes() method.
*
* @param aIncludeDefAttrs [in] points whether text attributes having default
* values of attributes should be included
* @param aSourceNode [in] the node we start to traverse from
* @param aStartOffset [in, out] the start offset
* @param aEndOffset [in, out] the end offset
* @param aAttributes [out] result attributes
*/
nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Set 'lang' text attribute and return range offsets where attibute is
* stretched. The method is used by GetTextAttributes() method.
*
* @param aIncludeDefAttrs [in] points whether text attributes having default
* values of attributes should be included
* @param aSourceNode [in] the node we start to traverse from
* @param aStartOffset [in, out] the start offset
* @param aEndOffset [in, out] the end offset
* @param aAttributes [out] result attributes
*/
nsresult GetLangTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Set CSS based text attribute and return range offsets where attibutes are
* stretched. The method is used by GetTextAttributes() method.
*
* @param aIncludeDefAttrs [in] points whether text attributes having default
* values of attributes should be included
* @param aSourceNode [in] the node we start to traverse from
* @param aStartOffset [in, out] the start offset
* @param aEndOffset [in, out] the end offset
* @param aAttributes [out] result attributes
*/
nsresult GetCSSTextAttributes(PRBool aIncludeDefAttrs,
nsIDOMNode *aSourceNode,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset,
nsIPersistentProperties *aAttributes);
/**
* Calculates range (start and end offsets) of text where the text attribute
* (pointed by nsTextAttr object) is stretched. New offsets may be smaller if
* the given text attribute changes its value before or after the given
* offsets.
*
* @param aNode [in] the node we start to traverse from
* @param aComparer [in] object used to describe the text attribute
* @param aStartHTOffset [in, out] the start offset
* @param aEndHTOffset [in, out] the end offset
*/
nsresult GetRangeForTextAttr(nsIDOMNode *aNode,
nsTextAttr *aComparer,
PRInt32 *aStartHTOffset,
PRInt32 *aEndHTOffset);
/**
* Find new end offset for text attributes navigating through the tree. New
* end offset may be smaller if the given text attribute (pointed by
* nsTextAttr object) changes its value before the given end offset.
*
* @param aCurrNode [in] the first node of the tree
* @param aComparer [in] object used to describe the text attribute
* @param aHTOffset [in, out] the end offset
* @return true if the end offset has been changed
*/
PRBool FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset);
/**
* Find the start offset for text attributes navigating through the tree. New
* start offset may be bigger if the given text attribute (pointed by
* nsTextAttr object) changes its value after the given start offset.
*
* @param aCurrNode [in] the node navigating through thee thee is started
* from
* @param aPrevNode [in] the previous node placed before the start node
* @param aComparer [in] object used to describe the text attribute
* @param aHTOffset [in, out] the start offset
* @return true if the start offset has been changed
*/
PRBool FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
nsIDOMNode *aPrevNode,
nsTextAttr *aComparer,
PRInt32 *aHTOffset);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,

View File

@ -48,6 +48,7 @@
#include "nsIAccessibleTypes.h"
#include "nsIWinAccessNode.h"
#include "nsAccessNodeWrap.h"
#include "nsAccessibleWrap.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@ -108,32 +109,26 @@ __try {
GET_NSIACCESSIBLETEXT
nsCOMPtr<nsIAccessible> accessible;
PRInt32 startOffset = 0, endOffset = 0;
textAcc->GetAttributeRange(aOffset, &startOffset, &endOffset,
getter_AddRefs(accessible));
if (!accessible)
return E_FAIL;
nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(accessible));
if (!winAccessNode)
return E_FAIL;
void *instancePtr = 0;
winAccessNode->QueryNativeInterface(IID_IAccessible2, &instancePtr);
if (!instancePtr)
return E_FAIL;
IAccessible2 *pAccessible2 = static_cast<IAccessible2*>(instancePtr);
HRESULT hr = pAccessible2->get_attributes(aTextAttributes);
pAccessible2->Release();
nsCOMPtr<nsIPersistentProperties> attributes;
nsresult rv = textAcc->GetTextAttributes(PR_TRUE, aOffset,
&startOffset, &endOffset,
getter_AddRefs(attributes));
if (NS_FAILED(rv))
return GetHRESULT(rv);
HRESULT hr = nsAccessibleWrap::ConvertToIA2Attributes(attributes,
aTextAttributes);
if (FAILED(hr))
return hr;
*aStartOffset = startOffset;
*aEndOffset = endOffset;
return hr;
return S_OK;
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return E_NOTIMPL;
return E_FAIL;
}
STDMETHODIMP

View File

@ -1584,60 +1584,7 @@ __try {
if (NS_FAILED(rv))
return GetHRESULT(rv);
if (!attributes)
return S_FALSE;
nsCOMPtr<nsISimpleEnumerator> propEnum;
attributes->Enumerate(getter_AddRefs(propEnum));
if (!propEnum)
return E_FAIL;
nsAutoString strAttrs;
const char kCharsToEscape[] = ":;=,\\";
PRBool hasMore = PR_FALSE;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> propSupports;
propEnum->GetNext(getter_AddRefs(propSupports));
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports));
if (!propElem)
return E_FAIL;
nsCAutoString name;
rv = propElem->GetKey(name);
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRUint32 offset = 0;
while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
name.Insert('\\', offset);
offset += 2;
}
nsAutoString value;
rv = propElem->GetValue(value);
if (NS_FAILED(rv))
return E_FAIL;
offset = 0;
while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
value.Insert('\\', offset);
offset += 2;
}
AppendUTF8toUTF16(name, strAttrs);
strAttrs.Append(':');
strAttrs.Append(value);
strAttrs.Append(';');
}
if (strAttrs.IsEmpty())
return S_FALSE;
*aAttributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length());
return *aAttributes ? S_OK : E_OUTOFMEMORY;
return ConvertToIA2Attributes(attributes, aAttributes);
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return E_FAIL;
@ -1829,6 +1776,69 @@ nsAccessibleWrap::GetHWNDFor(nsIAccessible *aAccessible)
return hWnd;
}
HRESULT
nsAccessibleWrap::ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
BSTR *aIA2Attributes)
{
*aIA2Attributes = NULL;
// The format is name:value;name:value; with \ for escaping these
// characters ":;=,\".
if (!aAttributes)
return S_FALSE;
nsCOMPtr<nsISimpleEnumerator> propEnum;
aAttributes->Enumerate(getter_AddRefs(propEnum));
if (!propEnum)
return E_FAIL;
nsAutoString strAttrs;
const char kCharsToEscape[] = ":;=,\\";
PRBool hasMore = PR_FALSE;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> propSupports;
propEnum->GetNext(getter_AddRefs(propSupports));
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports));
if (!propElem)
return E_FAIL;
nsCAutoString name;
if (NS_FAILED(propElem->GetKey(name)))
return E_FAIL;
PRUint32 offset = 0;
while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
name.Insert('\\', offset);
offset += 2;
}
nsAutoString value;
if (NS_FAILED(propElem->GetValue(value)))
return E_FAIL;
offset = 0;
while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
value.Insert('\\', offset);
offset += 2;
}
AppendUTF8toUTF16(name, strAttrs);
strAttrs.Append(':');
strAttrs.Append(value);
strAttrs.Append(';');
}
if (strAttrs.IsEmpty())
return S_FALSE;
*aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length());
return *aIA2Attributes ? S_OK : E_OUTOFMEMORY;
}
IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
{
if (!aXPAccessible) {

View File

@ -296,6 +296,8 @@ class nsAccessibleWrap : public nsAccessible,
// Helper methods
static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
static HWND GetHWNDFor(nsIAccessible *aAccessible);
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
BSTR *aIA2Attributes);
/**
* System caret support: update the Windows caret position.

View File

@ -69,6 +69,7 @@ _TEST_FILES =\
test_nsIAccessibleHyperText.html \
test_nsIAccessibleImage.html \
test_nsOuterDocAccessible.html \
test_textattrs.html \
test_textboxes.html \
test_textboxes.xul \
testTextboxes.js \

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Brett Wilson <brettw@gmail.com>
* Alexander Surkov <surkov.alexander@gmail.com>
*
* 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"),
@ -47,9 +48,18 @@ interface nsIDOMRange;
[ptr] native RangeArray(nsCOMArray<nsIDOMRange>);
[scriptable, uuid(b515878d-3b06-433b-bc9e-5c53d2fa3eff)]
[scriptable, uuid(5d21d5fe-3691-4716-a334-4691eea54d29)]
interface nsISelection2 : nsISelection
{
/**
* Returns the type of the selection (see nsISelectionController for
* available constants).
*/
readonly attribute short type;
/**
* Return array of ranges intersecting with the given DOM interval.
*/
void GetRangesForInterval(
in nsIDOMNode beginNode, in PRInt32 beginOffset,
in nsIDOMNode endNode, in PRInt32 endOffset,

View File

@ -4696,6 +4696,15 @@ nsTypedSelection::MoveIndexToNextMismatch(PRInt32* aIndex, nsIDOMNode* aNode,
return NS_OK;
}
NS_IMETHODIMP
nsTypedSelection::GetType(PRInt16 *aType)
{
NS_ENSURE_ARG_POINTER(aType);
*aType = mType;
return NS_OK;
}
// nsTypedSelection::GetRangesForInterval
//
// XPCOM wrapper for the COMArray version