bug 371394 - Fire text changed event when content of hypertext accessible is changed, r=ginn.chen

This commit is contained in:
surkov.alexander@gmail.com 2007-07-25 04:54:15 -07:00
Родитель 45fceee769
Коммит b6ab7ae518
7 изменённых файлов: 232 добавлений и 246 удалений

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

@ -400,8 +400,6 @@ NS_IMETHODIMP nsDocAccessible::GetDocument(nsIDOMDocument **aDOMDoc)
void nsDocAccessible::SetEditor(nsIEditor* aEditor)
{
mEditor = aEditor;
if (mEditor)
mEditor->AddEditActionListener(this);
}
void nsDocAccessible::CheckForEditor()
@ -533,10 +531,7 @@ NS_IMETHODIMP nsDocAccessible::Shutdown()
nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShellTreeItemFor(mDOMNode);
ShutdownChildDocuments(treeItem);
if (mEditor) {
mEditor->RemoveEditActionListener(this);
mEditor = nsnull;
}
mEditor = nsnull;
if (mDocLoadTimer) {
mDocLoadTimer->Cancel();
@ -1126,14 +1121,17 @@ void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
// InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
// unless an accessible can be created for the passed in node, which it
// can't do unless the node is visible. The right thing happens there so
// no need for an extra visibility check here.
PRUint32 childCount = aContainer->GetChildCount();
for (PRUint32 index = aNewIndexInContainer; index < childCount; index ++) {
InvalidateCacheSubtree(aContainer->GetChildAt(index),
nsIAccessibleEvent::EVENT_SHOW);
nsCOMPtr<nsIContent> child(aContainer->GetChildAt(index));
FireTextChangedEventOnDOMNodeInserted(child, aContainer, index);
// InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
// unless an accessible can be created for the passed in node, which it
// can't do unless the node is visible. The right thing happens there so
// no need for an extra visibility check here.
InvalidateCacheSubtree(child, nsIAccessibleEvent::EVENT_SHOW);
}
}
@ -1154,13 +1152,15 @@ void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
// XXX fire text change events here? See bug 377891
FireTextChangedEventOnDOMCharacterDataModified(aContent, aInfo);
}
void
nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, PRInt32 aIndexInContainer)
{
FireTextChangedEventOnDOMNodeInserted(aChild, aContainer, aIndexInContainer);
// InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
// unless an accessible can be created for the passed in node, which it
// can't do unless the node is visible. The right thing happens there so
@ -1172,6 +1172,9 @@ void
nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, PRInt32 aIndexInContainer)
{
FireTextChangedEventOnDOMNodeRemoved(aChild, aContainer, aIndexInContainer);
// Invalidate the subtree of the removed element.
InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_HIDE);
}
@ -1180,6 +1183,158 @@ nsDocAccessible::ParentChainChanged(nsIContent *aContent)
{
}
void
nsDocAccessible::FireTextChangedEventOnDOMCharacterDataModified(nsIContent *aContent,
CharacterDataChangeInfo* aInfo)
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
if (!node)
return;
nsCOMPtr<nsIAccessible> accessible;
nsresult rv = GetAccessibleInParentChain(node, getter_AddRefs(accessible));
if (NS_FAILED(rv) || !accessible)
return;
nsRefPtr<nsHyperTextAccessible> textAccessible;
rv = accessible->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
getter_AddRefs(textAccessible));
if (NS_FAILED(rv) || !textAccessible)
return;
PRInt32 start = aInfo->mChangeStart;
PRUint32 end = aInfo->mChangeEnd;
PRInt32 length = end - start;
PRUint32 replaceLen = aInfo->mReplaceLength;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(node, start, &offset);
if (NS_FAILED(rv))
return;
// Text has been removed.
if (length > 0) {
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(accessible, offset, length, PR_FALSE);
textAccessible->FireAccessibleEvent(event);
}
// Text has been added.
if (replaceLen) {
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(accessible, offset, replaceLen, PR_TRUE);
textAccessible->FireAccessibleEvent(event);
}
}
void
nsDocAccessible::FireTextChangedEventOnDOMNodeInserted(nsIContent *aChild,
nsIContent *aContainer,
PRInt32 aIndexInContainer)
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aChild));
if (!node)
return;
nsCOMPtr<nsIAccessible> accessible;
nsresult rv = GetAccessibleInParentChain(node, getter_AddRefs(accessible));
if (NS_FAILED(rv) || !accessible)
return;
nsRefPtr<nsHyperTextAccessible> textAccessible;
rv = accessible->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
getter_AddRefs(textAccessible));
if (NS_FAILED(rv) || !textAccessible)
return;
PRUint32 length = 1;
if (aChild && aChild->IsNodeOfType(nsINode::eTEXT)) {
length = aChild->TextLength();
if (!length)
return;
} else {
// Don't fire event for the first html:br in an editor.
nsCOMPtr<nsIEditor> editor;
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
if (editor) {
PRBool isEmpty = PR_FALSE;
editor->GetDocumentIsEmpty(&isEmpty);
if (isEmpty)
return;
}
}
nsCOMPtr<nsIDOMNode> parentNode(do_QueryInterface(aContainer));
if (!parentNode)
return;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(parentNode, aIndexInContainer, &offset);
if (NS_FAILED(rv))
return;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(accessible, offset, length, PR_TRUE);
if (!event)
return;
textAccessible->FireAccessibleEvent(event);
}
void
nsDocAccessible::FireTextChangedEventOnDOMNodeRemoved(nsIContent *aChild,
nsIContent *aContainer,
PRInt32 aIndexInContainer)
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aChild));
if (!node)
return;
nsCOMPtr<nsIAccessible> accessible;
nsresult rv = GetAccessibleInParentChain(node, getter_AddRefs(accessible));
if (NS_FAILED(rv) || !accessible)
return;
nsRefPtr<nsHyperTextAccessible> textAccessible;
rv = accessible->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
getter_AddRefs(textAccessible));
if (NS_FAILED(rv) || !textAccessible)
return;
PRUint32 length = 1;
if (aChild && aChild->IsNodeOfType(nsINode::eTEXT)) {
length = aChild->TextLength();
if (!length)
return;
} else {
// Don't fire event for the last html:br in an editor.
nsCOMPtr<nsIEditor> editor;
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
if (editor) {
PRBool isEmpty = PR_FALSE;
editor->GetDocumentIsEmpty(&isEmpty);
if (isEmpty)
return;
}
}
nsCOMPtr<nsIDOMNode> parentNode(do_QueryInterface(aContainer));
if (!parentNode)
return;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(parentNode, aIndexInContainer, &offset);
if (NS_FAILED(rv))
return;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(accessible, offset, length, PR_FALSE);
if (!event)
return;
textAccessible->FireAccessibleEvent(event);
}
nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent,
nsIDOMNode *aDOMNode,
void *aData,

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

@ -135,6 +135,27 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
*/
void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
/**
* Fire text changed event for charackter data changed.
*/
void FireTextChangedEventOnDOMCharacterDataModified(nsIContent *aContent,
CharacterDataChangeInfo* aInfo);
/**
* Fire text changed event for the inserted element if it is inside a text
* accessible.
*/
void FireTextChangedEventOnDOMNodeInserted(nsIContent *aChild,
nsIContent *aContainer,
PRInt32 aIndexInContainer);
/**
* Fire text changed event for the removed element if it is inside a text
* accessible.
*/
void FireTextChangedEventOnDOMNodeRemoved(nsIContent *aChild,
nsIContent *aContainer,
PRInt32 aIndexInContainer);
nsAccessNodeHashtable mAccessNodeCache;
void *mWnd;
nsCOMPtr<nsIDocument> mDocument;

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

@ -383,10 +383,7 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::Init()
NS_IMETHODIMP nsHTMLTextFieldAccessible::Shutdown()
{
if (mEditor) {
mEditor->RemoveEditActionListener(this);
mEditor = nsnull;
}
mEditor = nsnull;
return nsHyperTextAccessibleWrap::Shutdown();
}
@ -548,8 +545,6 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::DoAction(PRUint8 index)
void nsHTMLTextFieldAccessible::SetEditor(nsIEditor* aEditor)
{
mEditor = aEditor;
if (mEditor)
mEditor->AddEditActionListener(this);
}
void nsHTMLTextFieldAccessible::CheckForEditor()

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

@ -70,6 +70,12 @@ nsresult nsHyperTextAccessible::QueryInterface(REFNSIID aIID, void** aInstancePt
{
*aInstancePtr = nsnull;
if (aIID.Equals(NS_GET_IID(nsHyperTextAccessible))) {
*aInstancePtr = static_cast<nsHyperTextAccessible*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(mDOMNode));
if (mDOMNode && !xulDoc) {
// We need XUL doc check for now because for now nsDocAccessible must inherit from nsHyperTextAccessible
@ -1145,204 +1151,6 @@ nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor)
return NS_OK;
}
/**
* nsIEditActionListener impl.
*/
NS_IMETHODIMP nsHyperTextAccessible::WillCreateNode(const nsAString& aTag,
nsIDOMNode *aParent, PRInt32 aPosition)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode,
nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent,
PRInt32 aPosition)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent,
PRInt32 aPosition, nsresult aResult)
{
InvalidateChildren();
PRInt32 start;
PRUint32 length = 1;
PRBool isInserted = PR_TRUE;
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
if (content && content->IsNodeOfType(nsINode::eTEXT)) {
length = content->TextLength();
if (!length)
return NS_OK;
}
else {
// Don't fire event for the first br
nsCOMPtr<nsIEditor> editor = GetEditor();
if (editor) {
PRBool isEmpty;
editor->GetDocumentIsEmpty(&isEmpty);
if (isEmpty) {
return NS_OK;
}
}
}
nsCOMPtr<nsIDOMNode> parentNode;
aNode->GetParentNode(getter_AddRefs(parentNode));
if (NS_FAILED(DOMPointToOffset(parentNode, aPosition, &start)))
return NS_OK;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(this, start, length, isInserted);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireAccessibleEvent(event);
}
NS_IMETHODIMP nsHyperTextAccessible::WillDeleteNode(nsIDOMNode *aChild)
{
PRInt32 start;
PRUint32 length = 1;
PRBool isInserted = PR_FALSE;
nsCOMPtr<nsIContent> content(do_QueryInterface(aChild));
if (content && content->IsNodeOfType(nsINode::eTEXT)) {
length = content->TextLength();
if (!length)
return NS_OK;
}
else {
// Don't fire event for the last br
nsCOMPtr<nsIEditor> editor = GetEditor();
if (editor) {
PRBool isEmpty;
editor->GetDocumentIsEmpty(&isEmpty);
if (isEmpty) {
return NS_OK;
}
}
}
nsCOMPtr<nsIDOMNode> parentNode;
aChild->GetParentNode(getter_AddRefs(parentNode));
nsCOMPtr<nsIContent> parentContent(do_QueryInterface(parentNode));
NS_ENSURE_TRUE(parentContent, NS_ERROR_FAILURE);
nsCOMPtr<nsIContent> childContent(do_QueryInterface(aChild));
NS_ENSURE_TRUE(childContent, NS_ERROR_FAILURE);
if (NS_FAILED(DOMPointToOffset(parentNode, parentContent->IndexOf(childContent), &start)))
return NS_OK;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(this, start, length, isInserted);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireAccessibleEvent(event);
}
NS_IMETHODIMP nsHyperTextAccessible::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
{
return InvalidateChildren();
}
NS_IMETHODIMP nsHyperTextAccessible::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset,
nsIDOMNode *aNewLeftNode, nsresult aResult)
{
return InvalidateChildren();
}
NS_IMETHODIMP nsHyperTextAccessible::WillJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode, nsIDOMNode *aParent)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode,
nsIDOMNode *aParent, nsresult aResult)
{
return InvalidateChildren();
}
NS_IMETHODIMP nsHyperTextAccessible::WillInsertText(nsIDOMCharacterData *aTextNode,
PRInt32 aOffset, const nsAString& aString)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset,
const nsAString& aString, nsresult aResult)
{
PRInt32 start;
PRUint32 length = aString.Length();
PRBool isInserted = PR_TRUE;
if (NS_FAILED(DOMPointToOffset(aTextNode, aOffset, &start)))
return NS_OK;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(this, start, length, isInserted);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireAccessibleEvent(event);
}
NS_IMETHODIMP nsHyperTextAccessible::WillDeleteText(nsIDOMCharacterData *aTextNode,
PRInt32 aOffset, PRInt32 aLength)
{
PRInt32 start;
PRUint32 length = aLength;
PRBool isInserted = PR_FALSE;
if (NS_FAILED(DOMPointToOffset(aTextNode, aOffset, &start)))
return NS_OK;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(this, start, length, isInserted);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireAccessibleEvent(event);
}
NS_IMETHODIMP nsHyperTextAccessible::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset,
PRInt32 aLength, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP nsHyperTextAccessible::WillDeleteSelection(nsISelection *aSelection)
// <input> & <textarea> fires this event while deleting text
// <editor> fires WillDeleteText/WillDeleteNode instead
// XXX Deal with > 1 selections
{
PRInt32 selectionStart, selectionEnd;
GetSelectionBounds(0, &selectionStart, &selectionEnd);
PRInt32 start = PR_MIN(selectionStart, selectionEnd);;
PRUint32 length = PR_ABS(selectionEnd - selectionStart);
PRBool isInserted = PR_FALSE;
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
new nsAccTextChangeEvent(this, start, length, isInserted);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireAccessibleEvent(event);
}
NS_IMETHODIMP nsHyperTextAccessible::DidDeleteSelection(nsISelection *aSelection)
{
return NS_OK;
}
/**
* =================== Caret & Selection ======================
*/

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

@ -45,7 +45,6 @@
#include "nsIAccessibleHyperText.h"
#include "nsIAccessibleEditableText.h"
#include "nsAccessibleEventData.h"
#include "nsIEditActionListener.h"
#include "nsIEditor.h"
#include "nsFrameSelection.h"
#include "nsISelectionController.h"
@ -57,14 +56,22 @@ enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 };
const PRUnichar kEmbeddedObjectChar = 0xfffc;
const PRUnichar kForcedNewLineChar = '\n';
#define NS_HYPERTEXTACCESSIBLE_IMPL_CID \
{ /* 245f3bc9-224f-4839-a92e-95239705f30b */ \
0x245f3bc9, \
0x224f, \
0x4839, \
{ 0xa9, 0x2e, 0x95, 0x23, 0x97, 0x05, 0xf3, 0x0b } \
}
/**
* Special Accessible that knows how contain both text and embedded objects
*/
class nsHyperTextAccessible : public nsAccessibleWrap,
public nsIAccessibleText,
public nsIAccessibleHyperText,
public nsIAccessibleEditableText,
public nsIEditActionListener
public nsIAccessibleEditableText
{
public:
nsHyperTextAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
@ -72,13 +79,32 @@ public:
NS_DECL_NSIACCESSIBLETEXT
NS_DECL_NSIACCESSIBLEHYPERTEXT
NS_DECL_NSIACCESSIBLEEDITABLETEXT
NS_DECL_NSIEDITACTIONLISTENER
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HYPERTEXTACCESSIBLE_IMPL_CID)
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
void CacheChildren();
/**
* Turn a DOM Node and offset into a character offset into this hypertext.
* Will look for closest match when the DOM node does not have an accessible
* object associated with it. Will return an offset for the end of
* the string if the node is not found.
*
* @param aNode - the node to look for
* @param aNodeOffset - the offset to look for
* @param aResultOffset - the character offset into the current
* nsHyperTextAccessible
* @param aFinalAccessible [optional] - returns the accessible child which
* contained the offset, if it is within
* the current nsHyperTextAccessible,
* otherwise it is set to nsnull.
*/
nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset,
PRInt32 *aResultOffset,
nsIAccessible **aFinalAccessible = nsnull);
protected:
PRBool IsHyperText();
@ -122,20 +148,7 @@ protected:
*/
nsIFrame* GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset, nsAString *aText = nsnull,
nsIFrame **aEndFrame = nsnull, nsIntRect *aBoundsRect = nsnull);
/**
* Turn a DOM Node and offset into a character offset into this hypertext. Will look for closest match
* when the DOM node does not have an accessible object associated with it.
* Will return an offset for the end of the string if the node is not found.
* @param aNode, the node to look for
* @param aNodeOffset, the offset to look for
* @param aResult, the character offset into the current nsHyperTextAccessible
* @param aFinalAccessible (optional), returns the accessible child which contained the offset,
* if it is within the current nsHyperTextAccessible, otherwise
* it is set to nsnull.
* @return failure/success code
*/
nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResultOffset,
nsIAccessible **aFinalAccessible = nsnull);
nsIntRect GetBoundsForString(nsIFrame *aFrame, PRInt32 aStartOffset, PRInt32 aLength);
// Editor helpers, subclasses of nsHyperTextAccessible may have editor
@ -147,4 +160,8 @@ protected:
nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,
NS_HYPERTEXTACCESSIBLE_IMPL_CID)
#endif // _nsHyperTextAccessible_H_

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

@ -364,12 +364,7 @@ nsXFormsEditableAccessible::GetEditor()
void
nsXFormsEditableAccessible::SetEditor(nsIEditor *aEditor)
{
if (mEditor)
mEditor->RemoveEditActionListener(this);
mEditor = aEditor;
if (mEditor)
mEditor->AddEditActionListener(this);
}
// nsXFormsSelectableAccessible

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

@ -774,10 +774,7 @@ NS_IMETHODIMP nsXULTextFieldAccessible::Init()
NS_IMETHODIMP nsXULTextFieldAccessible::Shutdown()
{
if (mEditor) {
mEditor->RemoveEditActionListener(this);
mEditor = nsnull;
}
mEditor = nsnull;
return nsHyperTextAccessibleWrap::Shutdown();
}
@ -941,8 +938,6 @@ nsXULTextFieldAccessible::GetAllowsAnonChildAccessibles(PRBool *aAllowsAnonChild
void nsXULTextFieldAccessible::SetEditor(nsIEditor* aEditor)
{
mEditor = aEditor;
if (mEditor)
mEditor->AddEditActionListener(this);
}
void nsXULTextFieldAccessible::CheckForEditor()