Bug 949518 - keep caret offset syncronized with accessible tree, r=surkov

This commit is contained in:
Jonathan Wei 2014-05-01 11:45:40 -04:00
parent b107d7de01
commit fa66f8ae6a
9 changed files with 90 additions and 9 deletions

View File

@ -364,6 +364,11 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
logging::FocusNotificationTarget("fire focus event", "Target", target);
#endif
// Reset cached caret value. The cache will be updated upon processing the
// next caret move event. This ensures that we will return the correct caret
// offset before the caret move event is handled.
SelectionMgr()->ResetCaretOffset();
nsRefPtr<AccEvent> focusEvent =
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
nsEventShell::FireEvent(focusEvent);

View File

@ -6,6 +6,8 @@
#include "mozilla/a11y/SelectionManager.h"
#include "DocAccessible-inl.h"
#include "HyperTextAccessible.h"
#include "HyperTextAccessible-inl.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
@ -36,6 +38,12 @@ private:
~SelData() {}
};
SelectionManager::SelectionManager() :
mCaretOffset(-1), mAccWithCaret(nullptr)
{
}
void
SelectionManager::ClearControlSelectionListener()
{
@ -144,10 +152,13 @@ SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent)
if (!caretCntr)
return;
int32_t caretOffset = caretCntr->CaretOffset();
if (caretOffset != -1) {
Selection* selection = caretCntr->DOMSelection();
mCaretOffset = caretCntr->DOMPointToOffset(selection->GetFocusNode(),
selection->FocusOffset());
mAccWithCaret = caretCntr;
if (mCaretOffset != -1) {
nsRefPtr<AccCaretMoveEvent> caretMoveEvent =
new AccCaretMoveEvent(caretCntr, caretOffset, aEvent->FromUserInput());
new AccCaretMoveEvent(caretCntr, mCaretOffset, aEvent->FromUserInput());
nsEventShell::FireEvent(caretMoveEvent);
}
}

View File

@ -20,6 +20,7 @@ class Element;
namespace a11y {
class AccEvent;
class HyperTextAccessible;
/**
* This special accessibility class is for the caret and selection management.
@ -81,7 +82,38 @@ public:
*/
void ProcessTextSelChangeEvent(AccEvent* aEvent);
/**
* Gets the current caret offset/hypertext accessible pair. If there is no
* current pair, then returns -1 for the offset and a nullptr for the
* accessible.
*/
inline HyperTextAccessible* AccessibleWithCaret(int32_t* aCaret)
{
if (aCaret)
*aCaret = mCaretOffset;
return mAccWithCaret;
}
/**
* Update caret offset when it doesn't go through a caret move event.
*/
inline void UpdateCaretOffset(HyperTextAccessible* aItem, int32_t aOffset)
{
mAccWithCaret = aItem;
mCaretOffset = aOffset;
}
inline void ResetCaretOffset()
{
mCaretOffset = -1;
mAccWithCaret = nullptr;
}
protected:
SelectionManager();
/**
* Process DOM selection change. Fire selection and caret move events.
*/
@ -90,6 +122,8 @@ protected:
private:
// Currently focused control.
nsWeakFrame mCurrCtrlFrame;
int32_t mCaretOffset;
HyperTextAccessible* mAccWithCaret;
};
} // namespace a11y

View File

@ -2453,6 +2453,8 @@ Accessible::Shutdown()
mContent = nullptr;
mDoc = nullptr;
if (SelectionMgr()->AccessibleWithCaret(nullptr) == this)
SelectionMgr()->ResetCaretOffset();
}
// Accessible protected

View File

@ -32,6 +32,15 @@ HyperTextAccessible::IsValidRange(int32_t aStartOffset, int32_t aEndOffset)
endOffset <= CharacterCount();
}
inline void
HyperTextAccessible::SetCaretOffset(int32_t aOffset)
{
SetSelectionRange(aOffset, aOffset);
// XXX: Force cache refresh until a good solution for AT emulation of user
// input is implemented (AccessFu caret movement).
SelectionMgr()->UpdateCaretOffset(this, aOffset);
}
inline bool
HyperTextAccessible::AddToSelection(int32_t aStartOffset, int32_t aEndOffset)
{

View File

@ -1162,6 +1162,22 @@ HyperTextAccessible::CaretOffset() const
return -1;
}
// Check cached value.
int32_t caretOffset = -1;
HyperTextAccessible* text = SelectionMgr()->AccessibleWithCaret(&caretOffset);
// Use cached value if it corresponds to this accessible.
if (caretOffset != -1) {
if (text == this)
return caretOffset;
nsINode* textNode = text->GetNode();
// Ignore offset if cached accessible isn't a text leaf.
if (nsCoreUtils::IsAncestorOf(GetNode(), textNode))
return TransformOffset(text,
textNode->IsNodeOfType(nsINode::eTEXT) ? caretOffset : 0, false);
}
// No caret if the focused node is not inside this DOM node and this DOM node
// is not inside of focused node.
FocusManager::FocusDisposition focusDisp =

View File

@ -302,7 +302,7 @@ public:
* Get/set caret offset, if no caret then -1.
*/
int32_t CaretOffset() const;
void SetCaretOffset(int32_t aOffset) { SetSelectionRange(aOffset, aOffset); }
void SetCaretOffset(int32_t aOffset);
/**
* Provide the line number for the caret.
@ -406,6 +406,11 @@ public:
*/
virtual already_AddRefed<nsIEditor> GetEditor() const;
/**
* Return DOM selection object for the accessible.
*/
dom::Selection* DOMSelection() const;
protected:
// Accessible
virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
@ -486,10 +491,9 @@ protected:
// Selection helpers
/**
* Return frame/DOM selection object for the accessible.
* Return frame selection object for the accessible.
*/
already_AddRefed<nsFrameSelection> FrameSelection() const;
dom::Selection* DOMSelection() const;
/**
* Return selection ranges within the accessible subtree.

View File

@ -1766,8 +1766,7 @@ function textSelectionChecker(aID, aStartOffset, aEndOffset)
this.check = function textSelectionChecker_check(aEvent)
{
if (aStartOffset == aEndOffset) {
is(getAccessible(aID, [nsIAccessibleText]).caretOffset, aStartOffset,
"Wrong collapsed selection!");
ok(true, "Collapsed selection triggered text selection change event.");
} else {
testTextGetSelection(aID, aStartOffset, aEndOffset, 0);
}

View File

@ -46,7 +46,8 @@
new textSelectionChecker("ta1", 0, 1),
{ shiftKey: true }));
gQueue.push(new synthLeftKey("ta1",
new textSelectionChecker("ta1", 0, 0)));
[new textSelectionChecker("ta1", 0, 0),
new caretMoveChecker(0, "ta1")]));
gQueue.invoke(); // Will call SimpleTest.finish();
}