actions.js (5934B)
1 /* import-globals-from common.js */ 2 /* import-globals-from events.js */ 3 4 // ////////////////////////////////////////////////////////////////////////////// 5 // Event constants 6 7 const MOUSEDOWN_EVENT = 1; 8 const MOUSEUP_EVENT = 2; 9 const CLICK_EVENT = 4; 10 const COMMAND_EVENT = 8; 11 const FOCUS_EVENT = 16; 12 13 const CLICK_EVENTS = MOUSEDOWN_EVENT | MOUSEUP_EVENT | CLICK_EVENT; 14 const XUL_EVENTS = CLICK_EVENTS | COMMAND_EVENT; 15 16 // ////////////////////////////////////////////////////////////////////////////// 17 // Public functions 18 19 /** 20 * Test default accessible actions. 21 * 22 * Action tester interface is: 23 * 24 * ```js 25 * var actionObj = { 26 * // identifier of accessible to perform an action on 27 * get ID() {}, 28 * 29 * // index of the action 30 * get actionIndex() {}, 31 * 32 * // name of the action 33 * get actionName() {}, 34 * 35 * // DOM events (see constants defined above) 36 * get events() {}, 37 * 38 * // [optional] identifier of target DOM events listeners are registered on, 39 * // used with 'events', if missing then 'ID' is used instead. 40 * get targetID() {}, 41 * 42 * // [optional] true to match DOM events bubbled up to the target, 43 * // false (default) to only match events fired directly on the target. 44 * get allowBubbling() {}, 45 * 46 * // [optional] perform checks when 'click' event is handled if 'events' 47 * // is used. 48 * checkOnClickEvent: function() {}, 49 * 50 * // [optional] an array of invoker's checker objects (see eventQueue 51 * // constructor events.js) 52 * get eventSeq() {} 53 * }; 54 * ``` 55 * 56 * @param aArray [in] an array of action cheker objects 57 */ 58 function testActions(aArray) { 59 gActionsQueue = new eventQueue(); 60 61 for (var idx = 0; idx < aArray.length; idx++) { 62 var actionObj = aArray[idx]; 63 var accOrElmOrID = actionObj.ID; 64 var actionIndex = actionObj.actionIndex; 65 var actionName = actionObj.actionName; 66 var events = actionObj.events; 67 var accOrElmOrIDOfTarget = actionObj.targetID 68 ? actionObj.targetID 69 : accOrElmOrID; 70 71 var eventSeq = []; 72 if (events) { 73 var elm = getNode(accOrElmOrIDOfTarget); 74 if (events & MOUSEDOWN_EVENT) { 75 eventSeq.push(new checkerOfActionInvoker("mousedown", elm, actionObj)); 76 } 77 78 if (events & MOUSEUP_EVENT) { 79 eventSeq.push(new checkerOfActionInvoker("mouseup", elm, actionObj)); 80 } 81 82 if (events & CLICK_EVENT) { 83 eventSeq.push(new checkerOfActionInvoker("click", elm, actionObj)); 84 } 85 86 if (events & COMMAND_EVENT) { 87 eventSeq.push(new checkerOfActionInvoker("command", elm, actionObj)); 88 } 89 90 if (events & FOCUS_EVENT) { 91 eventSeq.push(new focusChecker(elm)); 92 } 93 } 94 95 if (actionObj.eventSeq) { 96 eventSeq = eventSeq.concat(actionObj.eventSeq); 97 } 98 99 var invoker = new actionInvoker( 100 accOrElmOrID, 101 actionIndex, 102 actionName, 103 eventSeq 104 ); 105 gActionsQueue.push(invoker); 106 } 107 108 gActionsQueue.invoke(); 109 } 110 111 /** 112 * Test action names and descriptions. 113 */ 114 function testActionNames(aID, aActions) { 115 var actions = typeof aActions == "string" ? [aActions] : aActions || []; 116 117 var acc = getAccessible(aID); 118 is(acc.actionCount, actions.length, "Wrong number of actions."); 119 for (var i = 0; i < actions.length; i++) { 120 is( 121 acc.getActionName(i), 122 actions[i], 123 "Wrong action name at " + i + " index." 124 ); 125 is( 126 acc.getActionDescription(0), 127 gActionDescrMap[actions[i]], 128 "Wrong action description at " + i + "index." 129 ); 130 } 131 } 132 133 // ////////////////////////////////////////////////////////////////////////////// 134 // Private 135 136 var gActionsQueue = null; 137 138 function actionInvoker(aAccOrElmOrId, aActionIndex, aActionName, aEventSeq) { 139 this.invoke = function actionInvoker_invoke() { 140 var acc = getAccessible(aAccOrElmOrId); 141 if (!acc) { 142 return INVOKER_ACTION_FAILED; 143 } 144 145 var isThereActions = acc.actionCount > 0; 146 ok( 147 isThereActions, 148 "No actions on the accessible for " + prettyName(aAccOrElmOrId) 149 ); 150 151 if (!isThereActions) { 152 return INVOKER_ACTION_FAILED; 153 } 154 155 is( 156 acc.getActionName(aActionIndex), 157 aActionName, 158 "Wrong action name of the accessible for " + prettyName(aAccOrElmOrId) 159 ); 160 161 try { 162 acc.doAction(aActionIndex); 163 } catch (e) { 164 ok(false, "doAction(" + aActionIndex + ") failed with: " + e.name); 165 return INVOKER_ACTION_FAILED; 166 } 167 return null; 168 }; 169 170 this.eventSeq = aEventSeq; 171 172 this.getID = function actionInvoker_getID() { 173 return ( 174 "invoke an action " + 175 aActionName + 176 " at index " + 177 aActionIndex + 178 " on " + 179 prettyName(aAccOrElmOrId) 180 ); 181 }; 182 } 183 184 function checkerOfActionInvoker(aType, aTarget, aActionObj) { 185 this.type = aType; 186 187 this.target = aTarget; 188 189 if (aActionObj && "eventTarget" in aActionObj) { 190 this.eventTarget = aActionObj.eventTarget; 191 } 192 193 if (aActionObj && aActionObj.allowBubbling) { 194 // Normally, we add event listeners on the document. To catch bubbled 195 // events, we need to add the listener on the target itself. 196 this.eventTarget = "element"; 197 // Normally, we only match an event fired directly on the target. Override 198 // this to match a bubbled event. 199 this.match = function (aEvent) { 200 return aEvent.currentTarget == aTarget; 201 }; 202 } 203 204 this.phase = false; 205 206 this.getID = function getID() { 207 return aType + " event handling"; 208 }; 209 210 this.check = function check(aEvent) { 211 if (aType == "click" && aActionObj && "checkOnClickEvent" in aActionObj) { 212 aActionObj.checkOnClickEvent(aEvent); 213 } 214 }; 215 } 216 217 var gActionDescrMap = { 218 jump: "Jump", 219 press: "Press", 220 check: "Check", 221 uncheck: "Uncheck", 222 select: "Select", 223 open: "Open", 224 close: "Close", 225 switch: "Switch", 226 click: "Click", 227 collapse: "Collapse", 228 expand: "Expand", 229 activate: "Activate", 230 cycle: "Cycle", 231 clickAncestor: "Click ancestor", 232 };