Bug 1053462 - Add isFromUserInput to vc change events and pivot methods. r=surkov
authorEitan Isaacson <eitan@monotonous.org>
Thu, 14 Aug 2014 21:44:59 -0400
changeset 221289 abb6a2af7cb508d2d745f6016a51ca55e380e5ca
parent 221287 150d3813133c7b30a5c8cd5f9b4afd630d3d0f36
child 221290 74f2a2d657e38df442564bccd3ba9fcd329234cd
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov
bugs1053462
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1053462 - Add isFromUserInput to vc change events and pivot methods. r=surkov
accessible/base/AccEvent.cpp
accessible/base/AccEvent.h
accessible/base/nsAccessiblePivot.cpp
accessible/base/nsAccessiblePivot.h
accessible/generic/DocAccessible.cpp
accessible/interfaces/nsIAccessiblePivot.idl
accessible/tests/mochitest/pivot.js
accessible/tests/mochitest/pivot/test_virtualcursor.html
accessible/tests/mochitest/pivot/test_virtualcursor_text.html
--- a/accessible/base/AccEvent.cpp
+++ b/accessible/base/AccEvent.cpp
@@ -181,18 +181,19 @@ AccTableChangeEvent::
 ////////////////////////////////////////////////////////////////////////////////
 // AccVCChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 AccVCChangeEvent::
   AccVCChangeEvent(Accessible* aAccessible,
                    nsIAccessible* aOldAccessible,
                    int32_t aOldStart, int32_t aOldEnd,
-                   int16_t aReason) :
-    AccEvent(::nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, aAccessible),
+                   int16_t aReason, EIsFromUserInput aIsFromUserInput) :
+    AccEvent(::nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, aAccessible,
+             aIsFromUserInput),
     mOldAccessible(aOldAccessible), mOldStart(aOldStart), mOldEnd(aOldEnd),
     mReason(aReason)
 {
 }
 
 already_AddRefed<nsIAccessibleEvent>
 a11y::MakeXPCEvent(AccEvent* aEvent)
 {
--- a/accessible/base/AccEvent.h
+++ b/accessible/base/AccEvent.h
@@ -464,17 +464,18 @@ private:
  * Accessible virtual cursor change event.
  */
 class AccVCChangeEvent : public AccEvent
 {
 public:
   AccVCChangeEvent(Accessible* aAccessible,
                    nsIAccessible* aOldAccessible,
                    int32_t aOldStart, int32_t aOldEnd,
-                   int16_t aReason);
+                   int16_t aReason,
+                   EIsFromUserInput aIsFromUserInput = eFromUserInput);
 
   virtual ~AccVCChangeEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eVirtualCursorChangeEvent;
   virtual unsigned int GetEventGroups() const
   {
     return AccEvent::GetEventGroups() | (1U << eVirtualCursorChangeEvent);
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -96,17 +96,17 @@ nsAccessiblePivot::SetPosition(nsIAccess
       return NS_ERROR_INVALID_ARG;
   }
 
   // Swap old position with new position, saves us an AddRef/Release.
   mPosition.swap(secondPosition);
   int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   mStartOffset = mEndOffset = -1;
   NotifyOfPivotChange(secondPosition, oldStart, oldEnd,
-                      nsIAccessiblePivot::REASON_NONE);
+                      nsIAccessiblePivot::REASON_NONE, false);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot)
 {
   NS_ENSURE_ARG_POINTER(aModalRoot);
@@ -149,17 +149,18 @@ nsAccessiblePivot::GetEndOffset(int32_t*
 
   *aEndOffset = mEndOffset;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
-                                int32_t aStartOffset, int32_t aEndOffset)
+                                int32_t aStartOffset, int32_t aEndOffset,
+                                bool aIsFromUserInput, uint8_t aArgc)
 {
   NS_ENSURE_ARG(aTextAccessible);
 
   // Check that start offset is smaller than end offset, and that if a value is
   // smaller than 0, both should be -1.
   NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
                  (aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
                  NS_ERROR_INVALID_ARG);
@@ -181,27 +182,28 @@ nsAccessiblePivot::SetTextRange(nsIAcces
   int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   mStartOffset = aStartOffset;
   mEndOffset = aEndOffset;
 
   nsRefPtr<Accessible> oldPosition = mPosition.forget();
   mPosition = newPosition;
 
   NotifyOfPivotChange(oldPosition, oldStart, oldEnd,
-                      nsIAccessiblePivot::REASON_TEXT);
+                      nsIAccessiblePivot::REASON_TEXT,
+                      (aArgc > 0) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 // Traversal functions
 
 NS_IMETHODIMP
 nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
                             nsIAccessible* aAnchor, bool aIncludeStart,
-                            uint8_t aArgc, bool* aResult)
+                            bool aIsFromUserInput, uint8_t aArgc, bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
   NS_ENSURE_ARG(aRule);
 
   *aResult = false;
 
   Accessible* root = GetActiveRoot();
   nsRefPtr<Accessible> anchor =
@@ -210,25 +212,26 @@ nsAccessiblePivot::MoveNext(nsIAccessibl
     return NS_ERROR_NOT_IN_TREE;
 
   nsresult rv = NS_OK;
   Accessible* accessible =
     SearchForward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (accessible)
-    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_NEXT);
+    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_NEXT,
+                                 (aArgc > 2) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
                                 nsIAccessible* aAnchor,
-                                bool aIncludeStart,
+                                bool aIncludeStart, bool aIsFromUserInput,
                                 uint8_t aArgc, bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
   NS_ENSURE_ARG(aRule);
 
   *aResult = false;
 
   Accessible* root = GetActiveRoot();
@@ -238,43 +241,48 @@ nsAccessiblePivot::MovePrevious(nsIAcces
     return NS_ERROR_NOT_IN_TREE;
 
   nsresult rv = NS_OK;
   Accessible* accessible =
     SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (accessible)
-    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV);
+    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV,
+                                 (aArgc > 2) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
+nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule,
+                             bool aIsFromUserInput,
+                             uint8_t aArgc, bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
   NS_ENSURE_ARG(aRule);
 
   Accessible* root = GetActiveRoot();
   NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
 
   nsresult rv = NS_OK;
   Accessible* accessible = SearchForward(root, aRule, true, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (accessible)
-    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST);
+    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST,
+                                 (aArgc > 0) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
-                            bool* aResult)
+                            bool aIsFromUserInput,
+                            uint8_t aArgc, bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
   NS_ENSURE_ARG(aRule);
 
   Accessible* root = GetActiveRoot();
   NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
 
   *aResult = false;
@@ -286,23 +294,26 @@ nsAccessiblePivot::MoveLast(nsIAccessibl
   while (lastAccessible->HasChildren())
     lastAccessible = lastAccessible->LastChild();
 
   // Search backwards from last accessible and find the last occurrence in the doc
   accessible = SearchBackward(lastAccessible, aRule, true, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (accessible)
-    *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST);
+    *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST,
+                                 (aArgc > 0) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
+nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary,
+                                  bool aIsFromUserInput, uint8_t aArgc,
+                                  bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
 
   *aResult = false;
 
   int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
   Accessible* tempPosition = mPosition;
   Accessible* root = GetActiveRoot();
@@ -402,23 +413,26 @@ nsAccessiblePivot::MoveNextByText(TextBo
     *aResult = true;
 
     Accessible* startPosition = mPosition;
     int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
     mPosition = tempPosition;
     mStartOffset = tempStart;
     mEndOffset = tempEnd;
     NotifyOfPivotChange(startPosition, oldStart, oldEnd,
-                        nsIAccessiblePivot::REASON_TEXT);
+                        nsIAccessiblePivot::REASON_TEXT,
+                        (aArgc > 0) ? aIsFromUserInput : true);
     return NS_OK;
   }
 }
 
 NS_IMETHODIMP
-nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
+nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary,
+                                      bool aIsFromUserInput, uint8_t aArgc,
+                                      bool* aResult)
 {
   NS_ENSURE_ARG(aResult);
 
   *aResult = false;
 
   int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
   Accessible* tempPosition = mPosition;
   Accessible* root = GetActiveRoot();
@@ -531,24 +545,26 @@ nsAccessiblePivot::MovePreviousByText(Te
 
     Accessible* startPosition = mPosition;
     int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
     mPosition = tempPosition;
     mStartOffset = tempStart;
     mEndOffset = tempEnd;
 
     NotifyOfPivotChange(startPosition, oldStart, oldEnd,
-                        nsIAccessiblePivot::REASON_TEXT);
+                        nsIAccessiblePivot::REASON_TEXT,
+                        (aArgc > 0) ? aIsFromUserInput : true);
     return NS_OK;
   }
 }
 
 NS_IMETHODIMP
 nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule,
                                int32_t aX, int32_t aY, bool aIgnoreNoMatch,
+                               bool aIsFromUserInput, uint8_t aArgc,
                                bool* aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
   NS_ENSURE_ARG_POINTER(aRule);
 
   *aResult = false;
 
   Accessible* root = GetActiveRoot();
@@ -576,17 +592,18 @@ nsAccessiblePivot::MoveToPoint(nsIAccess
           aY >= childY && aY < childY + childHeight)
         match = child;
     }
 
     child = child->Parent();
   }
 
   if (match || !aIgnoreNoMatch)
-    *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT);
+    *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT,
+                                 (aArgc > 0) ? aIsFromUserInput : true);
 
   return NS_OK;
 }
 
 // Observer functions
 
 NS_IMETHODIMP
 nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
@@ -621,24 +638,26 @@ nsAccessiblePivot::IsDescendantOf(Access
       return true;
   } while ((accessible = accessible->Parent()));
 
   return false;
 }
 
 bool
 nsAccessiblePivot::MovePivotInternal(Accessible* aPosition,
-                                     PivotMoveReason aReason)
+                                     PivotMoveReason aReason,
+                                     bool aIsFromUserInput)
 {
   nsRefPtr<Accessible> oldPosition = mPosition.forget();
   mPosition = aPosition;
   int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   mStartOffset = mEndOffset = -1;
 
-  return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason);
+  return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason,
+                             aIsFromUserInput);
 }
 
 Accessible*
 nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible,
                                        RuleCache& aCache,
                                        uint16_t* aFilterResult,
                                        nsresult* aResult)
 {
@@ -819,26 +838,27 @@ nsAccessiblePivot::SearchForText(Accessi
 
   return nullptr;
 }
 
 
 bool
 nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition,
                                        int32_t aOldStart, int32_t aOldEnd,
-                                       int16_t aReason)
+                                       int16_t aReason, bool aIsFromUserInput)
 {
   if (aOldPosition == mPosition &&
       aOldStart == mStartOffset && aOldEnd == mEndOffset)
     return false;
 
   nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
     nsIAccessiblePivotObserver* obs = iter.GetNext();
-    obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd, aReason);
+    obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd, aReason,
+                        aIsFromUserInput);
   }
 
   return true;
 }
 
 nsresult
 RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult)
 {
--- a/accessible/base/nsAccessiblePivot.h
+++ b/accessible/base/nsAccessiblePivot.h
@@ -44,17 +44,18 @@ private:
   void operator = (const nsAccessiblePivot&) MOZ_DELETE;
 
   /*
    * Notify all observers on a pivot change. Return true if it has changed and
    * observers have been notified.
    */
   bool NotifyOfPivotChange(Accessible* aOldAccessible,
                            int32_t aOldStart, int32_t aOldEnd,
-                           PivotMoveReason aReason);
+                           PivotMoveReason aReason,
+                           bool aIsFromUserInput);
 
   /*
    * Check to see that the given accessible is a descendant of given ancestor
    */
   bool IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor);
 
 
   /*
@@ -90,17 +91,18 @@ private:
     }
 
     return mRoot;
   }
 
   /*
    * Update the pivot, and notify observers. Return true if it moved.
    */
-  bool MovePivotInternal(Accessible* aPosition, PivotMoveReason aReason);
+  bool MovePivotInternal(Accessible* aPosition, PivotMoveReason aReason,
+                         bool aIsFromUserInput);
 
   /*
    * Get initial node we should start a search from with a given rule.
    *
    * When we do a move operation from one position to another,
    * the initial position can be inside of a subtree that is ignored by
    * the given rule. We need to step out of the ignored subtree and start
    * the search from there.
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -811,21 +811,22 @@ DocAccessible::Observe(nsISupports* aSub
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessiblePivotObserver
 
 NS_IMETHODIMP
 DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
                               nsIAccessible* aOldAccessible,
                               int32_t aOldStart, int32_t aOldEnd,
-                              PivotMoveReason aReason)
+                              PivotMoveReason aReason,
+                              bool aIsFromUserInput)
 {
-  nsRefPtr<AccEvent> event = new AccVCChangeEvent(this, aOldAccessible,
-                                                  aOldStart, aOldEnd,
-                                                  aReason);
+  nsRefPtr<AccEvent> event = new AccVCChangeEvent(
+    this, aOldAccessible, aOldStart, aOldEnd, aReason,
+    aIsFromUserInput ? eFromUserInput : eNoUserInput);
   nsEventShell::FireEvent(event);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIDocumentObserver
 
--- a/accessible/interfaces/nsIAccessiblePivot.idl
+++ b/accessible/interfaces/nsIAccessiblePivot.idl
@@ -15,17 +15,17 @@ interface nsIAccessibleTraversalRule;
 interface nsIAccessiblePivotObserver;
 
 /**
  * The pivot interface encapsulates a reference to a single place in an accessible
  * subtree. The pivot is a point or a range in the accessible tree. This interface
  * provides traversal methods to move the pivot to next/prev state that complies 
  * to a given rule.
  */
-[scriptable, uuid(c2938033-e240-4fe5-9cb6-e7ad649ccd10)]
+[scriptable, uuid(81fe5144-059b-42db-bd3a-f6ce3158d5e9)]
 interface nsIAccessiblePivot : nsISupports
 {
   const TextBoundaryType CHAR_BOUNDARY = 0;
   const TextBoundaryType WORD_BOUNDARY = 1;
   const TextBoundaryType LINE_BOUNDARY = 2;
   const TextBoundaryType ATTRIBUTE_RANGE_BOUNDARY = 3;
 
   const PivotMoveReason REASON_NONE = 0;
@@ -59,101 +59,124 @@ interface nsIAccessiblePivot : nsISuppor
   /**
    * The end offset of the text range the pivot points at, otherwise -1.
    */
   readonly attribute long endOffset;
 
   /**
    * Set the pivot's text range in a text accessible.
    *
-   * @param aTextAccessible [in] the text accessible that contains the desired
-   *                        range.
-   * @param aStartOffset    [in] the start offset to set.
-   * @param aEndOffset      [in] the end offset to set.
+   * @param aTextAccessible  [in] the text accessible that contains the desired
+   *                           range.
+   * @param aStartOffset     [in] the start offset to set.
+   * @param aEndOffset       [in] the end offset to set.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @throws NS_ERROR_INVALID_ARG when the offset exceeds the accessible's
    *   character count.
    */
-  void setTextRange(in nsIAccessibleText aTextAccessible,
-                    in long aStartOffset, in long aEndOffset);
+  [optional_argc] void setTextRange(in nsIAccessibleText aTextAccessible,
+                                    in long aStartOffset, in long aEndOffset,
+                                    [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to next object, from current position or given anchor,
    * complying to given traversal rule.
    *
-   * @param aRule         [in] traversal rule to use.
-   * @param aAnchor       [in] accessible to start search from, if not provided,
+   * @param aRule            [in] traversal rule to use.
+   * @param aAnchor          [in] accessible to start search from, if not provided,
    *                           current position will be used.
-   * @param aIncludeStart [in] include anchor accessible in search.
+   * @param aIncludeStart    [in] include anchor accessible in search.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if there are no new nodes to traverse to.
    */
   [optional_argc] boolean moveNext(in nsIAccessibleTraversalRule aRule,
                                    [optional] in nsIAccessible aAnchor,
-                                   [optional] in boolean aIncludeStart);
+                                   [optional] in boolean aIncludeStart,
+                                   [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to previous object, from current position or given anchor,
    * complying to given traversal rule.
    *
-   * @param aRule         [in] traversal rule to use.
-   * @param aAnchor       [in] accessible to start search from, if not provided,
+   * @param aRule            [in] traversal rule to use.
+   * @param aAnchor          [in] accessible to start search from, if not provided,
    *                           current position will be used.
-   * @param aIncludeStart [in] include anchor accessible in search.
+   * @param aIncludeStart    [in] include anchor accessible in search.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if there are no new nodes to traverse to.
    */
   [optional_argc] boolean movePrevious(in nsIAccessibleTraversalRule aRule,
                                        [optional] in nsIAccessible aAnchor,
-                                       [optional] in boolean aIncludeStart);
+                                       [optional] in boolean aIncludeStart,
+                                       [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to first object in subtree complying to given traversal rule.
    *
-   * @param aRule [in] traversal rule to use.
+   * @param aRule            [in] traversal rule to use.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if there are no new nodes to traverse to.
    */
-  boolean moveFirst(in nsIAccessibleTraversalRule aRule);
+  [optional_argc] boolean moveFirst(in nsIAccessibleTraversalRule aRule,
+                                    [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to last object in subtree complying to given traversal rule.
    *
-   * @param aRule [in] traversal rule to use.
-   * @return true on success, false if there are no new nodes to traverse to.
+   * @param aRule            [in] traversal rule to use.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    */
-  boolean moveLast(in nsIAccessibleTraversalRule aRule);
+  [optional_argc] boolean moveLast(in nsIAccessibleTraversalRule aRule,
+                                   [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to next text range.
    *
-   * @param aBoundary [in] type of boundary for next text range, character, word,
-   *                  etc.
+   * @param aBoundary        [in] type of boundary for next text range,
+   *                           character, word, etc.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if there are is no more text.
    */
-  boolean moveNextByText(in TextBoundaryType aBoundary);
-  
+  [optional_argc] boolean moveNextByText(in TextBoundaryType aBoundary,
+                                         [optional] in boolean aIsFromUserInput);
+
   /**
    * Move pivot to previous text range.
    *
-   * @param aBoundary [in] type of boundary for previous text range, character,
-   *                  word, etc.
+   * @param aBoundary        [in] type of boundary for next text range,
+   *                           character, word, etc.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if there are is no more text.
    */
-  boolean movePreviousByText(in TextBoundaryType aBoundary);
+  [optional_argc] boolean movePreviousByText(in TextBoundaryType aBoundary,
+                                             [optional] in boolean aIsFromUserInput);
 
   /**
    * Move pivot to given coordinate in screen pixels.
    *
-   * @param aRule          [in]  raversal rule to use.
-   * @param aX             [in]  screen's x coordinate
-   * @param aY             [in]  screen's y coordinate
-   * @param aIgnoreNoMatch [in]  don't unset position if no object was found at
-   *                       point.
+   * @param aRule            [in]  raversal rule to use.
+   * @param aX               [in]  screen's x coordinate
+   * @param aY               [in]  screen's y coordinate
+   * @param aIgnoreNoMatch   [in]  don't unset position if no object was found
+   *                           at point.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    * @return true on success, false if the pivot has not been moved.
    */
-  boolean moveToPoint(in nsIAccessibleTraversalRule aRule,
-                      in long aX, in long aY,
-                      in boolean aIgnoreNoMatch);
+  [optional_argc] boolean moveToPoint(in nsIAccessibleTraversalRule aRule,
+                                      in long aX, in long aY,
+                                      in boolean aIgnoreNoMatch,
+                                      [optional] in boolean aIsFromUserInput);
 
   /**
    * Add an observer for pivot changes.
    *
    * @param aObserver [in] the observer object to be notified of pivot changes.
    */
   void addObserver(in nsIAccessiblePivotObserver aObserver);
 
@@ -169,29 +192,33 @@ interface nsIAccessiblePivot : nsISuppor
  * An observer interface for pivot changes.
  */
 [scriptable, uuid(b6508c5e-c081-467d-835c-613eedf9ee9b)]
 interface nsIAccessiblePivotObserver : nsISupports
 {
   /**
    * Called when the pivot changes.
    *
-   * @param aPivot         [in] the pivot that has changed.
-   * @param aOldAccessible [in] the old pivot position before the change, or null.
-   * @param aOldStart      [in] the old start offset, or -1.
-   * @param aOldEnd        [in] the old end offset, or -1.
-   * @param aReason        [in] the reason for the pivot change.
+   * @param aPivot           [in] the pivot that has changed.
+   * @param aOldAccessible   [in] the old pivot position before the change,
+   *                           or null.
+   * @param aOldStart        [in] the old start offset, or -1.
+   * @param aOldEnd          [in] the old end offset, or -1.
+   * @param aReason          [in] the reason for the pivot change.
+   * @param aIsFromUserInput [in] the pivot changed because of direct user input
+   *                           (default is true).
    */
   void onPivotChanged(in nsIAccessiblePivot aPivot,
                       in nsIAccessible aOldAccessible,
                       in long aOldStart, in long aOldEnd,
-                      in PivotMoveReason aReason);
+                      in PivotMoveReason aReason,
+                      in boolean aIsFromUserInput);
 };
 
-[scriptable, uuid(4d9c4352-20f5-4c54-9580-0c77bb6b1115)]
+[scriptable, uuid(e197460d-1eff-4247-b4bb-a43be1840dae)]
 interface nsIAccessibleTraversalRule : nsISupports
 {
   /* Ignore this accessible object */
   const unsigned short FILTER_IGNORE = 0x0;
   /* Accept this accessible object */
   const unsigned short FILTER_MATCH = 0x1;
   /* Don't traverse accessibles children */
   const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
--- a/accessible/tests/mochitest/pivot.js
+++ b/accessible/tests/mochitest/pivot.js
@@ -70,21 +70,22 @@ var ObjectTraversalRule =
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 // Virtual state invokers and checkers
 
 /**
  * A checker for virtual cursor changed events.
  */
-function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod)
+function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod,
+                          aIsFromUserInput)
 {
   this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
 
-  this.match = function VCChangedChecker_check(aEvent)
+  this.match = function VCChangedChecker_match(aEvent)
   {
     var event = null;
     try {
       event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
     } catch (e) {
       return false;
     }
 
@@ -109,16 +110,19 @@ function VCChangedChecker(aDocAcc, aIdOr
     var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
     var nameMatches = position && position.name == aIdOrNameOrAcc;
     var accMatches = position == aIdOrNameOrAcc;
 
     SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
                   "expecting " + aIdOrNameOrAcc + ", got '" +
                   prettyName(position));
 
+    SimpleTest.is(aEvent.isFromUserInput, aIsFromUserInput,
+                  "Expected user input is " + aIsFromUserInput + '\n');
+
     if (aTextOffsets) {
       SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
                     "wrong start offset");
       SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
                     "wrong end offset");
     }
 
     var prevPosAndOffset = VCChangedChecker.
@@ -185,55 +189,72 @@ function setVCRangeInvoker(aDocAcc, aTex
 
   this.getID = function setVCRangeInvoker_getID()
   {
     return "Set offset in " + prettyName(aTextAccessible) +
       " to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
   };
 
   this.eventSeq = [
-    new VCChangedChecker(aDocAcc, aTextAccessible, aTextOffsets, "setTextRange")
+    new VCChangedChecker(aDocAcc, aTextAccessible, aTextOffsets, "setTextRange", true)
   ];
 }
 
 /**
  * Move the pivot and wait for virtual cursor change event.
  *
  * @param aDocAcc          [in] document that manages the virtual cursor
  * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
  * @param aRule            [in] traversal rule object
  * @param aIdOrNameOrAcc   [in] id, accessible or accessible name to expect
  *                         virtual cursor to land on after performing move method.
  *                         false if no move is expected.
+ * @param aIsFromUserInput [in] set user input flag when invoking method, and
+ *                         expect it in the event.
  */
-function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc)
+function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc,
+                         aIsFromUserInput)
 {
   var expectMove = (aIdOrNameOrAcc != false);
   this.invoke = function virtualCursorChangedInvoker_invoke()
   {
     VCChangedChecker.
       storePreviousPosAndOffset(aDocAcc.virtualCursor);
     if (aPivotMoveMethod && aRule) {
-      var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
+      var moved = false;
+      switch (aPivotMoveMethod) {
+        case 'moveFirst':
+        case 'moveLast':
+          moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule,
+            aIsFromUserInput === undefined ? true : aIsFromUserInput);
+          break;
+        case 'moveNext':
+        case 'movePrevious':
+          moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule,
+            aDocAcc.virtualCursor.position, false,
+            aIsFromUserInput === undefined ? true : aIsFromUserInput);
+          break;
+      }
       SimpleTest.is(!!moved, !!expectMove,
                     "moved pivot with " + aPivotMoveMethod +
                     " to " + aIdOrNameOrAcc);
     } else {
       aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
     }
   };
 
   this.getID = function setVCPosInvoker_getID()
   {
     return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
   };
 
   if (expectMove) {
     this.eventSeq = [
-      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, aPivotMoveMethod)
+      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, aPivotMoveMethod,
+        aIsFromUserInput === undefined ? !!aPivotMoveMethod : aIsFromUserInput)
     ];
   } else {
     this.eventSeq = [];
     this.unexpectedEventSeq = [
       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
     ];
   }
 }
@@ -244,40 +265,45 @@ function setVCPosInvoker(aDocAcc, aPivot
  * @param aDocAcc          [in] document that manages the virtual cursor
  * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
  * @param aBoundary        [in] boundary constant
  * @param aTextOffsets     [in] start and end offsets of text range to set in
  *                         virtual cursor.
  * @param aIdOrNameOrAcc   [in] id, accessible or accessible name to expect
  *                         virtual cursor to land on after performing move method.
  *                         false if no move is expected.
+ * @param aIsFromUserInput [in] set user input flag when invoking method, and
+ *                         expect it in the event.
  */
-function setVCTextInvoker(aDocAcc, aPivotMoveMethod, aBoundary, aTextOffsets, aIdOrNameOrAcc)
+function setVCTextInvoker(aDocAcc, aPivotMoveMethod, aBoundary, aTextOffsets,
+                          aIdOrNameOrAcc, aIsFromUserInput)
 {
   var expectMove = (aIdOrNameOrAcc != false);
   this.invoke = function virtualCursorChangedInvoker_invoke()
   {
     VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
     SimpleTest.info(aDocAcc.virtualCursor.position);
-    var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aBoundary);
+    var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aBoundary,
+      aIsFromUserInput === undefined ? true : false);
     SimpleTest.is(!!moved, !!expectMove,
                   "moved pivot by text with " + aPivotMoveMethod +
                   " to " + aIdOrNameOrAcc);
   };
 
   this.getID = function setVCPosInvoker_getID()
   {
     return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod + " in " +
       prettyName(aIdOrNameOrAcc) + ", " + boundaryToString(aBoundary) +
       ", [" + aTextOffsets + "]";
   };
 
   if (expectMove) {
     this.eventSeq = [
-      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod)
+      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod,
+        aIsFromUserInput === undefined ? true : aIsFromUserInput)
     ];
   } else {
     this.eventSeq = [];
     this.unexpectedEventSeq = [
       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
     ];
   }
 }
@@ -312,17 +338,17 @@ function moveVCCoordInvoker(aDocAcc, aX,
 
   this.getID = function setVCPosInvoker_getID()
   {
     return "Do " + (expectMove ? "" : "no-op ") + "moveToPoint " + aIdOrNameOrAcc;
   };
 
   if (expectMove) {
     this.eventSeq = [
-      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, 'moveToPoint')
+      new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, 'moveToPoint', true)
     ];
   } else {
     this.eventSeq = [];
     this.unexpectedEventSeq = [
       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
     ];
   }
 }
@@ -402,17 +428,18 @@ function queueTraversalSequence(aQueue, 
   aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
 
   aQueue.push(new setVCPosInvoker(aDocAcc, "moveLast", aRule,
                                   aSequence[aSequence.length - 1]));
 
   // No further more matches for given rule, expect no virtual cursor changes.
   aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
 
-  aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
+  // set isFromUserInput to false, just to test..
+  aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0], false));
 
   // No previous more matches for given rule, expect no virtual cursor changes.
   aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
 
   // Remove modal root (if any).
   aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
 }
 
--- a/accessible/tests/mochitest/pivot/test_virtualcursor.html
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -91,18 +91,20 @@
         getAccessible(doc.getElementById('paragraph-1')),
         ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
          'semper', ' nulla. ']);
 
       gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
                                           NS_ERROR_INVALID_ARG));
 
       // Put cursor in an ignored subtree
+      // set isFromUserInput to false, just to test..
       gQueue.push(new setVCPosInvoker(docAcc, null, null,
-                                      getAccessible(doc.getElementById("hidden-link"))));
+                                      getAccessible(doc.getElementById("hidden-link")),
+                                      false));
       // Next item shoud be outside of that subtree
       gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "An "));
 
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
--- a/accessible/tests/mochitest/pivot/test_virtualcursor_text.html
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor_text.html
@@ -48,27 +48,31 @@
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [5,7],
                   getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3],
                   getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
                   getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3],
                   getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
+      // set user input to false, and see if it works
       gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [5,7],
-                  getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+                  getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)),
+                  false);
 
       gQueue.push(new setVCPosInvoker(docAcc, null, null,
                                       getAccessible(doc.getElementById('section-1'))));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,1],
                   getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,9],
                   getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+      // set user input to false, and see if it works
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
-                  getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+                  getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText),
+                  false));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,6],
                   getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,12],
                   getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,2],
                   getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
       gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19],
                   getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));