accessible/tests/mochitest/pivot.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
     3 ////////////////////////////////////////////////////////////////////////////////
     4 // Constants
     6 const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
     7 const PREFILTER_ARIA_HIDDEN = nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN;
     8 const PREFILTER_TRANSPARENT = nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
     9 const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
    10 const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
    11 const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
    12 const CHAR_BOUNDARY = nsIAccessiblePivot.CHAR_BOUNDARY;
    13 const WORD_BOUNDARY = nsIAccessiblePivot.WORD_BOUNDARY;
    15 const NS_ERROR_NOT_IN_TREE = 0x80780026;
    16 const NS_ERROR_INVALID_ARG = 0x80070057;
    18 ////////////////////////////////////////////////////////////////////////////////
    19 // Traversal rules
    21 /**
    22  * Rule object to traverse all focusable nodes and text nodes.
    23  */
    24 var HeadersTraversalRule =
    25 {
    26   getMatchRoles: function(aRules)
    27   {
    28     aRules.value = [ROLE_HEADING];
    29     return aRules.value.length;
    30   },
    32   preFilter: PREFILTER_INVISIBLE,
    34   match: function(aAccessible)
    35   {
    36     return FILTER_MATCH;
    37   },
    39   QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
    40 }
    42 /**
    43  * Traversal rule for all focusable nodes or leafs.
    44  */
    45 var ObjectTraversalRule =
    46 {
    47   getMatchRoles: function(aRules)
    48   {
    49     aRules.value = [];
    50     return 0;
    51   },
    53   preFilter: PREFILTER_INVISIBLE | PREFILTER_ARIA_HIDDEN | PREFILTER_TRANSPARENT,
    55   match: function(aAccessible)
    56   {
    57     var rv = FILTER_IGNORE;
    58     var role = aAccessible.role;
    59     if (hasState(aAccessible, STATE_FOCUSABLE) &&
    60         (role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
    61       rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
    62     else if (aAccessible.childCount == 0 &&
    63              role != ROLE_STATICTEXT && aAccessible.name.trim())
    64       rv = FILTER_MATCH;
    66     return rv;
    67   },
    69   QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
    70 };
    72 ////////////////////////////////////////////////////////////////////////////////
    73 // Virtual state invokers and checkers
    75 /**
    76  * A checker for virtual cursor changed events.
    77  */
    78 function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod)
    79 {
    80   this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
    82   this.match = function VCChangedChecker_check(aEvent)
    83   {
    84     var event = null;
    85     try {
    86       event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
    87     } catch (e) {
    88       return false;
    89     }
    91     var expectedReason = VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
    92       nsIAccessiblePivot.REASON_NONE;
    94     return event.reason == expectedReason;
    95   };
    97   this.check = function VCChangedChecker_check(aEvent)
    98   {
    99     SimpleTest.info("VCChangedChecker_check");
   101     var event = null;
   102     try {
   103       event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
   104     } catch (e) {
   105       SimpleTest.ok(false, "Does not support correct interface: " + e);
   106     }
   108     var position = aDocAcc.virtualCursor.position;
   109     var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
   110     var nameMatches = position && position.name == aIdOrNameOrAcc;
   111     var accMatches = position == aIdOrNameOrAcc;
   113     SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
   114                   "expecting " + aIdOrNameOrAcc + ", got '" +
   115                   prettyName(position));
   117     if (aTextOffsets) {
   118       SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
   119                     "wrong start offset");
   120       SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
   121                     "wrong end offset");
   122     }
   124     var prevPosAndOffset = VCChangedChecker.
   125       getPreviousPosAndOffset(aDocAcc.virtualCursor);
   127     if (prevPosAndOffset) {
   128       SimpleTest.is(event.oldAccessible, prevPosAndOffset.position,
   129                     "previous position does not match");
   130       SimpleTest.is(event.oldStartOffset, prevPosAndOffset.startOffset,
   131                     "previous start offset does not match");
   132       SimpleTest.is(event.oldEndOffset, prevPosAndOffset.endOffset,
   133                     "previous end offset does not match");
   134     }
   135   };
   136 }
   138 VCChangedChecker.prevPosAndOffset = {};
   140 VCChangedChecker.storePreviousPosAndOffset =
   141   function storePreviousPosAndOffset(aPivot)
   142 {
   143   VCChangedChecker.prevPosAndOffset[aPivot] =
   144     {position: aPivot.position,
   145      startOffset: aPivot.startOffset,
   146      endOffset: aPivot.endOffset};
   147 };
   149 VCChangedChecker.getPreviousPosAndOffset =
   150   function getPreviousPosAndOffset(aPivot)
   151 {
   152   return VCChangedChecker.prevPosAndOffset[aPivot];
   153 };
   155 VCChangedChecker.methodReasonMap = {
   156   'moveNext': nsIAccessiblePivot.REASON_NEXT,
   157   'movePrevious': nsIAccessiblePivot.REASON_PREV,
   158   'moveFirst': nsIAccessiblePivot.REASON_FIRST,
   159   'moveLast': nsIAccessiblePivot.REASON_LAST,
   160   'setTextRange': nsIAccessiblePivot.REASON_TEXT,
   161   'moveNextByText': nsIAccessiblePivot.REASON_TEXT,
   162   'movePreviousByText': nsIAccessiblePivot.REASON_TEXT,
   163   'moveToPoint': nsIAccessiblePivot.REASON_POINT
   164 };
   166 /**
   167  * Set a text range in the pivot and wait for virtual cursor change event.
   168  *
   169  * @param aDocAcc         [in] document that manages the virtual cursor
   170  * @param aTextAccessible [in] accessible to set to virtual cursor's position
   171  * @param aTextOffsets    [in] start and end offsets of text range to set in
   172  *                        virtual cursor.
   173  */
   174 function setVCRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
   175 {
   176   this.invoke = function virtualCursorChangedInvoker_invoke()
   177   {
   178     VCChangedChecker.
   179       storePreviousPosAndOffset(aDocAcc.virtualCursor);
   180     SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
   181     aDocAcc.virtualCursor.setTextRange(aTextAccessible,
   182                                        aTextOffsets[0],
   183                                        aTextOffsets[1]);
   184   };
   186   this.getID = function setVCRangeInvoker_getID()
   187   {
   188     return "Set offset in " + prettyName(aTextAccessible) +
   189       " to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
   190   };
   192   this.eventSeq = [
   193     new VCChangedChecker(aDocAcc, aTextAccessible, aTextOffsets, "setTextRange")
   194   ];
   195 }
   197 /**
   198  * Move the pivot and wait for virtual cursor change event.
   199  *
   200  * @param aDocAcc          [in] document that manages the virtual cursor
   201  * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
   202  * @param aRule            [in] traversal rule object
   203  * @param aIdOrNameOrAcc   [in] id, accessible or accessible name to expect
   204  *                         virtual cursor to land on after performing move method.
   205  *                         false if no move is expected.
   206  */
   207 function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc)
   208 {
   209   var expectMove = (aIdOrNameOrAcc != false);
   210   this.invoke = function virtualCursorChangedInvoker_invoke()
   211   {
   212     VCChangedChecker.
   213       storePreviousPosAndOffset(aDocAcc.virtualCursor);
   214     if (aPivotMoveMethod && aRule) {
   215       var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
   216       SimpleTest.is(!!moved, !!expectMove,
   217                     "moved pivot with " + aPivotMoveMethod +
   218                     " to " + aIdOrNameOrAcc);
   219     } else {
   220       aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
   221     }
   222   };
   224   this.getID = function setVCPosInvoker_getID()
   225   {
   226     return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
   227   };
   229   if (expectMove) {
   230     this.eventSeq = [
   231       new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, aPivotMoveMethod)
   232     ];
   233   } else {
   234     this.eventSeq = [];
   235     this.unexpectedEventSeq = [
   236       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
   237     ];
   238   }
   239 }
   241 /**
   242  * Move the pivot by text and wait for virtual cursor change event.
   243  *
   244  * @param aDocAcc          [in] document that manages the virtual cursor
   245  * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
   246  * @param aBoundary        [in] boundary constant
   247  * @param aTextOffsets     [in] start and end offsets of text range to set in
   248  *                         virtual cursor.
   249  * @param aIdOrNameOrAcc   [in] id, accessible or accessible name to expect
   250  *                         virtual cursor to land on after performing move method.
   251  *                         false if no move is expected.
   252  */
   253 function setVCTextInvoker(aDocAcc, aPivotMoveMethod, aBoundary, aTextOffsets, aIdOrNameOrAcc)
   254 {
   255   var expectMove = (aIdOrNameOrAcc != false);
   256   this.invoke = function virtualCursorChangedInvoker_invoke()
   257   {
   258     VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
   259     SimpleTest.info(aDocAcc.virtualCursor.position);
   260     var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aBoundary);
   261     SimpleTest.is(!!moved, !!expectMove,
   262                   "moved pivot by text with " + aPivotMoveMethod +
   263                   " to " + aIdOrNameOrAcc);
   264   };
   266   this.getID = function setVCPosInvoker_getID()
   267   {
   268     return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod + " in " +
   269       prettyName(aIdOrNameOrAcc) + ", " + boundaryToString(aBoundary) +
   270       ", [" + aTextOffsets + "]";
   271   };
   273   if (expectMove) {
   274     this.eventSeq = [
   275       new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod)
   276     ];
   277   } else {
   278     this.eventSeq = [];
   279     this.unexpectedEventSeq = [
   280       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
   281     ];
   282   }
   283 }
   286 /**
   287  * Move the pivot to the position under the point.
   288  *
   289  * @param aDocAcc        [in] document that manages the virtual cursor
   290  * @param aX             [in] screen x coordinate
   291  * @param aY             [in] screen y coordinate
   292  * @param aIgnoreNoMatch [in] don't unset position if no object was found at
   293  *                       point.
   294  * @param aRule          [in] traversal rule object
   295  * @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
   296  *                       virtual cursor to land on after performing move method.
   297  *                       false if no move is expected.
   298  */
   299 function moveVCCoordInvoker(aDocAcc, aX, aY, aIgnoreNoMatch,
   300                             aRule, aIdOrNameOrAcc)
   301 {
   302   var expectMove = (aIdOrNameOrAcc != false);
   303   this.invoke = function virtualCursorChangedInvoker_invoke()
   304   {
   305     VCChangedChecker.
   306       storePreviousPosAndOffset(aDocAcc.virtualCursor);
   307     var moved = aDocAcc.virtualCursor.moveToPoint(aRule, aX, aY,
   308                                                   aIgnoreNoMatch);
   309     SimpleTest.ok((expectMove && moved) || (!expectMove && !moved),
   310                   "moved pivot");
   311   };
   313   this.getID = function setVCPosInvoker_getID()
   314   {
   315     return "Do " + (expectMove ? "" : "no-op ") + "moveToPoint " + aIdOrNameOrAcc;
   316   };
   318   if (expectMove) {
   319     this.eventSeq = [
   320       new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, 'moveToPoint')
   321     ];
   322   } else {
   323     this.eventSeq = [];
   324     this.unexpectedEventSeq = [
   325       new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
   326     ];
   327   }
   328 }
   330 /**
   331  * Change the pivot modalRoot
   332  *
   333  * @param aDocAcc         [in] document that manages the virtual cursor
   334  * @param aModalRootAcc   [in] accessible of the modal root, or null
   335  * @param aExpectedResult [in] error result expected. 0 if expecting success
   336  */
   337 function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult)
   338 {
   339   this.invoke = function setModalRootInvoker_invoke()
   340   {
   341     var errorResult = 0;
   342     try {
   343       aDocAcc.virtualCursor.modalRoot = aModalRootAcc;
   344     } catch (x) {
   345       SimpleTest.ok(
   346         x.result, "Unexpected exception when changing modal root: " + x);
   347       errorResult = x.result;
   348     }
   350     SimpleTest.is(errorResult, aExpectedResult,
   351                   "Did not get expected result when changing modalRoot");
   352   };
   354   this.getID = function setModalRootInvoker_getID()
   355   {
   356     return "Set modalRoot to " + prettyName(aModalRootAcc);
   357   };
   359   this.eventSeq = [];
   360   this.unexpectedEventSeq = [
   361     new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
   362   ];
   363 }
   365 /**
   366  * Add invokers to a queue to test a rule and an expected sequence of element ids
   367  * or accessible names for that rule in the given document.
   368  *
   369  * @param aQueue     [in] event queue in which to push invoker sequence.
   370  * @param aDocAcc    [in] the managing document of the virtual cursor we are
   371  *                   testing
   372  * @param aRule      [in] the traversal rule to use in the invokers
   373  * @param aModalRoot [in] a modal root to use in this traversal sequence
   374  * @param aSequence  [in] a sequence of accessible names or element ids to expect
   375  *                   with the given rule in the given document
   376  */
   377 function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence)
   378 {
   379   aDocAcc.virtualCursor.position = null;
   381   // Add modal root (if any)
   382   aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
   384   aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
   386   for (var i = 1; i < aSequence.length; i++) {
   387     var invoker =
   388       new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
   389     aQueue.push(invoker);
   390   }
   392   // No further more matches for given rule, expect no virtual cursor changes.
   393   aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
   395   for (var i = aSequence.length-2; i >= 0; i--) {
   396     var invoker =
   397       new setVCPosInvoker(aDocAcc, "movePrevious", aRule, aSequence[i]);
   398     aQueue.push(invoker);
   399   }
   401   // No previous more matches for given rule, expect no virtual cursor changes.
   402   aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
   404   aQueue.push(new setVCPosInvoker(aDocAcc, "moveLast", aRule,
   405                                   aSequence[aSequence.length - 1]));
   407   // No further more matches for given rule, expect no virtual cursor changes.
   408   aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
   410   aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
   412   // No previous more matches for given rule, expect no virtual cursor changes.
   413   aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
   415   // Remove modal root (if any).
   416   aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
   417 }
   419 /**
   420  * A checker for removing an accessible while the virtual cursor is on it.
   421  */
   422 function removeVCPositionChecker(aDocAcc, aHiddenParentAcc)
   423 {
   424   this.__proto__ = new invokerChecker(EVENT_REORDER, aHiddenParentAcc);
   426   this.check = function removeVCPositionChecker_check(aEvent) {
   427     var errorResult = 0;
   428     try {
   429       aDocAcc.virtualCursor.moveNext(ObjectTraversalRule);
   430     } catch (x) {
   431       errorResult = x.result;
   432     }
   433     SimpleTest.is(
   434       errorResult, NS_ERROR_NOT_IN_TREE,
   435       "Expecting NOT_IN_TREE error when moving pivot from invalid position.");
   436   };
   437 }
   439 /**
   440  * Put the virtual cursor's position on an object, and then remove it.
   441  *
   442  * @param aDocAcc     [in] document that manages the virtual cursor
   443  * @param aPosNode    [in] DOM node to hide after virtual cursor's position is
   444  *                    set to it.
   445  */
   446 function removeVCPositionInvoker(aDocAcc, aPosNode)
   447 {
   448   this.accessible = getAccessible(aPosNode);
   449   this.invoke = function removeVCPositionInvoker_invoke()
   450   {
   451     aDocAcc.virtualCursor.position = this.accessible;
   452     aPosNode.parentNode.removeChild(aPosNode);
   453   };
   455   this.getID = function removeVCPositionInvoker_getID()
   456   {
   457     return "Bring virtual cursor to accessible, and remove its DOM node.";
   458   };
   460   this.eventSeq = [
   461     new removeVCPositionChecker(aDocAcc, this.accessible.parent)
   462   ];
   463 }
   465 /**
   466  * A checker for removing the pivot root and then calling moveFirst, and
   467  * checking that an exception is thrown.
   468  */
   469 function removeVCRootChecker(aPivot)
   470 {
   471   this.__proto__ = new invokerChecker(EVENT_REORDER, aPivot.root.parent);
   473   this.check = function removeVCRootChecker_check(aEvent) {
   474     var errorResult = 0;
   475     try {
   476       aPivot.moveLast(ObjectTraversalRule);
   477     } catch (x) {
   478       errorResult = x.result;
   479     }
   480     SimpleTest.is(
   481       errorResult, NS_ERROR_NOT_IN_TREE,
   482       "Expecting NOT_IN_TREE error when moving pivot from invalid position.");
   483   };
   484 }
   486 /**
   487  * Create a pivot, remove its root, and perform an operation where the root is
   488  * needed.
   489  *
   490  * @param aRootNode [in] DOM node of which accessible will be the root of the
   491  *                       pivot. Should have more than one child.
   492  */
   493 function removeVCRootInvoker(aRootNode)
   494 {
   495   this.pivot = gAccRetrieval.createAccessiblePivot(getAccessible(aRootNode));
   496   this.invoke = function removeVCRootInvoker_invoke()
   497   {
   498     this.pivot.position = this.pivot.root.firstChild;
   499     aRootNode.parentNode.removeChild(aRootNode);
   500   };
   502   this.getID = function removeVCRootInvoker_getID()
   503   {
   504     return "Remove root of pivot from tree.";
   505   };
   507   this.eventSeq = [
   508     new removeVCRootChecker(this.pivot)
   509   ];
   510 }
   512 /**
   513  * A debug utility for writing proper sequences for queueTraversalSequence.
   514  */
   515 function dumpTraversalSequence(aPivot, aRule)
   516 {
   517   var sequence = [];
   518   if (aPivot.moveFirst(aRule)) {
   519     do {
   520       sequence.push("'" + prettyName(aPivot.position) + "'");
   521     } while (aPivot.moveNext(aRule))
   522   }
   523   SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
   524 }

mercurial