head.js (3295B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 /* exported HeadersTraversalRule, ObjectTraversalRule, testPivotSequence, testFailsWithNotInTree */ 8 9 // Load the shared-head file first. 10 Services.scriptloader.loadSubScript( 11 "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", 12 this 13 ); 14 15 /* import-globals-from ../../mochitest/layout.js */ 16 /* import-globals-from ../../mochitest/role.js */ 17 /* import-globals-from ../../mochitest/states.js */ 18 loadScripts( 19 { name: "common.js", dir: MOCHITESTS_DIR }, 20 { name: "promisified-events.js", dir: MOCHITESTS_DIR }, 21 { name: "states.js", dir: MOCHITESTS_DIR }, 22 { name: "role.js", dir: MOCHITESTS_DIR }, 23 { name: "layout.js", dir: MOCHITESTS_DIR } 24 ); 25 26 const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH; 27 const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE; 28 const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE; 29 30 const NS_ERROR_NOT_IN_TREE = 0x80780026; 31 32 // ////////////////////////////////////////////////////////////////////////////// 33 // Traversal rules 34 35 /** 36 * Rule object to traverse all focusable nodes and text nodes. 37 */ 38 const HeadersTraversalRule = { 39 match(acc) { 40 return acc.role == ROLE_HEADING ? FILTER_MATCH : FILTER_IGNORE; 41 }, 42 43 QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]), 44 }; 45 46 /** 47 * Traversal rule for all focusable nodes or leafs. 48 */ 49 const ObjectTraversalRule = { 50 match(acc) { 51 let [state, extstate] = getStates(acc); 52 if (state & STATE_INVISIBLE) { 53 return FILTER_IGNORE; 54 } 55 56 if ((extstate & EXT_STATE_OPAQUE) == 0) { 57 return FILTER_IGNORE | FILTER_IGNORE_SUBTREE; 58 } 59 60 let rv = FILTER_IGNORE; 61 let role = acc.role; 62 if ( 63 hasState(acc, STATE_FOCUSABLE) && 64 role != ROLE_DOCUMENT && 65 role != ROLE_INTERNAL_FRAME 66 ) { 67 rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH; 68 } else if ( 69 acc.childCount == 0 && 70 role != ROLE_LISTITEM_MARKER && 71 acc.name.trim() 72 ) { 73 rv = FILTER_MATCH; 74 } 75 76 return rv; 77 }, 78 79 QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]), 80 }; 81 82 function getIdOrName(acc) { 83 let id = getAccessibleDOMNodeID(acc); 84 if (id) { 85 return id; 86 } 87 return acc.name; 88 } 89 90 function* pivotNextGenerator(pivot, rule) { 91 for (let acc = pivot.first(rule); acc; acc = pivot.next(acc, rule)) { 92 yield acc; 93 } 94 } 95 96 function* pivotPreviousGenerator(pivot, rule) { 97 for (let acc = pivot.last(rule); acc; acc = pivot.prev(acc, rule)) { 98 yield acc; 99 } 100 } 101 102 function testPivotSequence(pivot, rule, expectedSequence) { 103 is( 104 JSON.stringify([...pivotNextGenerator(pivot, rule)].map(getIdOrName)), 105 JSON.stringify(expectedSequence), 106 "Forward pivot sequence is correct" 107 ); 108 is( 109 JSON.stringify([...pivotPreviousGenerator(pivot, rule)].map(getIdOrName)), 110 JSON.stringify([...expectedSequence].reverse()), 111 "Reverse pivot sequence is correct" 112 ); 113 } 114 115 function testFailsWithNotInTree(func, msg) { 116 try { 117 func(); 118 ok(false, msg); 119 } catch (x) { 120 is(x.result, NS_ERROR_NOT_IN_TREE, `Expecting NOT_IN_TREE: ${msg}`); 121 } 122 }