Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 <?xml version="1.0"?>
2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
4 type="text/css"?>
6 <!-- Firefox searchbar -->
7 <?xml-stylesheet href="chrome://browser/content/browser.css"
8 type="text/css"?>
9 <!-- SeaMonkey searchbar -->
10 <?xml-stylesheet href="chrome://navigator/content/navigator.css"
11 type="text/css"?>
13 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
14 title="Accessible focus event testing">
16 <script type="application/javascript"
17 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
18 <script type="application/javascript"
19 src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
21 <script type="application/javascript"
22 src="../common.js" />
23 <script type="application/javascript"
24 src="../role.js" />
25 <script type="application/javascript"
26 src="../states.js" />
27 <script type="application/javascript"
28 src="../events.js" />
30 <script type="application/javascript"
31 src="../autocomplete.js" />
33 <script type="application/javascript">
34 <![CDATA[
35 ////////////////////////////////////////////////////////////////////////////
36 // Hacky stuffs
38 // This is the hack needed for searchbar work outside of browser.
39 function getBrowser()
40 {
41 return {
42 mCurrentBrowser: { engines: new Array() }
43 };
44 }
46 ////////////////////////////////////////////////////////////////////////////
47 // Invokers
49 function loadFormAutoComplete(aIFrameID)
50 {
51 this.iframeNode = getNode(aIFrameID);
52 this.iframe = getAccessible(aIFrameID);
54 this.eventSeq = [
55 new invokerChecker(EVENT_REORDER, this.iframe)
56 ];
58 this.invoke = function loadFormAutoComplete_invoke()
59 {
60 var url = "data:text/html,<html><body><form id='form'>" +
61 "<input id='input' name='a11ytest-formautocomplete'>" +
62 "</form></body></html>";
63 this.iframeNode.setAttribute("src", url);
64 }
66 this.getID = function loadFormAutoComplete_getID()
67 {
68 return "load form autocomplete page";
69 }
70 }
72 function initFormAutoCompleteBy(aIFrameID, aAutoCompleteValue)
73 {
74 this.iframe = getAccessible(aIFrameID);
76 this.eventSeq = [
77 new invokerChecker(EVENT_REORDER, this.iframe)
78 ];
80 this.invoke = function initFormAutoCompleteBy_invoke()
81 {
82 var iframeDOMDoc = getIFrameDOMDoc(aIFrameID);
84 var inputNode = iframeDOMDoc.getElementById("input");
85 inputNode.value = aAutoCompleteValue;
86 var formNode = iframeDOMDoc.getElementById("form");
87 formNode.submit();
88 }
90 this.getID = function initFormAutoCompleteBy_getID()
91 {
92 return "init form autocomplete by '" + aAutoCompleteValue + "'";
93 }
94 }
96 function loadHTML5ListAutoComplete(aIFrameID)
97 {
98 this.iframeNode = getNode(aIFrameID);
99 this.iframe = getAccessible(aIFrameID);
101 this.eventSeq = [
102 new invokerChecker(EVENT_REORDER, this.iframe)
103 ];
105 this.invoke = function loadHTML5ListAutoComplete_invoke()
106 {
107 var url = "data:text/html,<html><body>" +
108 "<datalist id='cities'><option>hello</option><option>hi</option></datalist>" +
109 "<input id='input' list='cities'>" +
110 "</body></html>";
111 this.iframeNode.setAttribute("src", url);
112 }
114 this.getID = function loadHTML5ListAutoComplete_getID()
115 {
116 return "load HTML5 list autocomplete page";
117 }
118 }
120 function removeChar(aID, aCheckerOrEventSeq)
121 {
122 this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
124 this.invoke = function removeChar_invoke()
125 {
126 synthesizeKey("VK_LEFT", { shiftKey: true });
127 synthesizeKey("VK_DELETE", {});
128 }
130 this.getID = function removeChar_getID()
131 {
132 return "remove char on " + prettyName(aID);
133 }
134 }
136 function replaceOnChar(aID, aChar, aCheckerOrEventSeq)
137 {
138 this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
140 this.invoke = function replaceOnChar_invoke()
141 {
142 this.DOMNode.select();
143 synthesizeKey(aChar, {});
144 }
146 this.getID = function replaceOnChar_getID()
147 {
148 return "replace on char '" + aChar + "' for" + prettyName(aID);
149 }
150 }
152 function focusOnMouseOver(aIDFunc, aIDFuncArg)
153 {
154 this.eventSeq = [ new focusChecker(aIDFunc, aIDFuncArg) ];
156 this.invoke = function focusOnMouseOver_invoke()
157 {
158 this.id = aIDFunc.call(null, aIDFuncArg);
159 this.node = getNode(this.id);
160 this.window = this.node.ownerDocument.defaultView;
162 if (this.node.localName == "tree") {
163 var tree = getAccessible(this.node);
164 var accessible = getAccessible(this.id);
165 if (tree != accessible) {
166 var itemX = {}, itemY = {}, treeX = {}, treeY = {};
167 accessible.getBounds(itemX, itemY, {}, {});
168 tree.getBounds(treeX, treeY, {}, {});
169 this.x = itemX.value - treeX.value;
170 this.y = itemY.value - treeY.value;
171 }
172 }
174 // Generate mouse move events in timeouts until autocomplete popup list
175 // doesn't have it, the reason is do that because autocomplete popup
176 // ignores mousemove events firing in too short range.
177 synthesizeMouse(this.node, this.x, this.y, { type: "mousemove" });
178 this.doMouseMoveFlood(this);
179 }
181 this.finalCheck = function focusOnMouseOver_getID()
182 {
183 this.isFlooding = false;
184 }
186 this.getID = function focusOnMouseOver_getID()
187 {
188 return "mouse over on " + prettyName(aIDFunc.call(null, aIDFuncArg));
189 }
191 this.doMouseMoveFlood = function focusOnMouseOver_doMouseMoveFlood(aThis)
192 {
193 synthesizeMouse(aThis.node, aThis.x + 1, aThis.y + 1,
194 { type: "mousemove" }, aThis.window);
196 if (aThis.isFlooding)
197 aThis.window.setTimeout(aThis.doMouseMoveFlood, 0, aThis);
198 }
200 this.id = null;
201 this.node = null;
202 this.window = null;
204 this.isFlooding = true;
205 this.x = 1;
206 this.y = 1;
207 }
209 function selectByClick(aIDFunc, aIDFuncArg,
210 aFocusTargetFunc, aFocusTargetFuncArg)
211 {
212 this.eventSeq = [ new focusChecker(aFocusTargetFunc, aFocusTargetFuncArg) ];
214 this.invoke = function selectByClick_invoke()
215 {
216 var id = aIDFunc.call(null, aIDFuncArg);
217 var node = getNode(id);
218 var targetWindow = node.ownerDocument.defaultView;
220 var x = 0, y = 0;
221 if (node.localName == "tree") {
222 var tree = getAccessible(node);
223 var accessible = getAccessible(id);
224 if (tree != accessible) {
225 var itemX = {}, itemY = {}, treeX = {}, treeY = {};
226 accessible.getBounds(itemX, itemY, {}, {});
227 tree.getBounds(treeX, treeY, {}, {});
228 x = itemX.value - treeX.value;
229 y = itemY.value - treeY.value;
230 }
231 }
233 synthesizeMouseAtCenter(node, {}, targetWindow);
234 }
236 this.getID = function selectByClick_getID()
237 {
238 return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
239 }
240 }
242 ////////////////////////////////////////////////////////////////////////////
243 // Target getters
245 function getItem(aItemObj)
246 {
247 var autocomplete = aItemObj.autocomplete;
248 var autocompleteNode = aItemObj.autocompleteNode;
250 // XUL searchbar
251 if (autocompleteNode.localName == "searchbar") {
252 var popupNode = autocompleteNode._popup;
253 if (popupNode) {
254 var list = getAccessible(popupNode);
255 return list.getChildAt(aItemObj.index);
256 }
257 }
259 // XUL autocomplete
260 var popupNode = autocompleteNode.popup;
261 if (!popupNode) {
262 // HTML form autocomplete
263 var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
264 getService(Components.interfaces.nsIAutoCompleteController);
265 popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
266 }
268 if (popupNode) {
269 if ("richlistbox" in popupNode) {
270 var list = getAccessible(popupNode.richlistbox);
271 return list.getChildAt(aItemObj.index);
272 }
274 var list = getAccessible(popupNode.tree);
275 return list.getChildAt(aItemObj.index + 1);
276 }
277 }
279 function getTextEntry(aID)
280 {
281 // For form autocompletes the autocomplete widget and text entry widget
282 // is the same widget, for XUL autocompletes the text entry is a first
283 // child.
284 var localName = getNode(aID).localName;
286 // XUL autocomplete
287 if (localName == "textbox")
288 return getAccessible(aID).firstChild;
290 // HTML form autocomplete
291 if (localName == "input")
292 return getAccessible(aID);
294 // XUL searchbar
295 if (localName == "searchbar")
296 return getAccessible(getNode(aID).textbox.inputField);
298 return null;
299 }
301 function itemObj(aID, aIdx)
302 {
303 this.autocompleteNode = getNode(aID);
305 this.autocomplete = this.autocompleteNode.localName == "searchbar" ?
306 getAccessible(this.autocompleteNode.textbox) :
307 getAccessible(this.autocompleteNode);
309 this.index = aIdx;
310 }
312 function getIFrameDOMDoc(aIFrameID)
313 {
314 return getNode(aIFrameID).contentDocument;
315 }
317 ////////////////////////////////////////////////////////////////////////////
318 // Test helpers
320 function queueAutoCompleteTests(aID)
321 {
322 // focus autocomplete text entry
323 gQueue.push(new synthFocus(aID, new focusChecker(getTextEntry, aID)));
325 // open autocomplete popup
326 gQueue.push(new synthDownKey(aID, new nofocusChecker()));
328 // select second option ('hi' option), focus on it
329 gQueue.push(new synthUpKey(aID,
330 new focusChecker(getItem, new itemObj(aID, 1))));
332 // choose selected option, focus on text entry
333 gQueue.push(new synthEnterKey(aID, new focusChecker(getTextEntry, aID)));
335 // remove char, autocomplete popup appears
336 gQueue.push(new removeChar(aID, new nofocusChecker()));
338 // select first option ('hello' option), focus on it
339 gQueue.push(new synthDownKey(aID,
340 new focusChecker(getItem, new itemObj(aID, 0))));
342 // mouse move on second option ('hi' option), focus on it
343 gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 1)));
345 // autocomplete popup updated (no selected item), focus on textentry
346 gQueue.push(new synthKey(aID, "e", null, new focusChecker(getTextEntry, aID)));
348 // select first option ('hello' option), focus on it
349 gQueue.push(new synthDownKey(aID,
350 new focusChecker(getItem, new itemObj(aID, 0))));
352 // popup gets hidden, focus on textentry
353 gQueue.push(new synthRightKey(aID, new focusChecker(getTextEntry, aID)));
355 // popup gets open, no focus
356 gQueue.push(new synthOpenComboboxKey(aID, new nofocusChecker()));
358 // select first option again ('hello' option), focus on it
359 gQueue.push(new synthDownKey(aID,
360 new focusChecker(getItem, new itemObj(aID, 0))));
362 // no option is selected, focus on text entry
363 gQueue.push(new synthUpKey(aID, new focusChecker(getTextEntry, aID)));
365 // close popup, no focus
366 gQueue.push(new synthEscapeKey(aID, new nofocusChecker()));
368 // autocomplete popup appears (no selected item), focus stays on textentry
369 gQueue.push(new replaceOnChar(aID, "h", new nofocusChecker()));
371 // mouse move on first option ('hello' option), focus on it
372 gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 0)));
374 // click first option ('hello' option), popup closes, focus on text entry
375 gQueue.push(new selectByClick(getItem, new itemObj(aID, 0), getTextEntry, aID));
376 }
378 ////////////////////////////////////////////////////////////////////////////
379 // Tests
381 //gA11yEventDumpID = "eventdump"; // debug stuff
382 //gA11yEventDumpToConsole = true; // debug stuff
384 var gInitQueue = null;
385 function initTests()
386 {
387 if (SEAMONKEY || MAC) {
388 todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237), and on Mac (bug 746177)");
389 shutdownAutoComplete();
390 SimpleTest.finish();
391 return;
392 }
394 gInitQueue = new eventQueue();
395 gInitQueue.push(new loadFormAutoComplete("iframe"));
396 gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
397 gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
398 gInitQueue.push(new loadHTML5ListAutoComplete("iframe2"));
399 gInitQueue.onFinish = function initQueue_onFinish()
400 {
401 SimpleTest.executeSoon(doTests);
402 return DO_NOT_FINISH_TEST;
403 }
404 gInitQueue.invoke();
405 }
407 var gQueue = null;
408 function doTests()
409 {
410 // Test focus events.
411 gQueue = new eventQueue();
413 ////////////////////////////////////////////////////////////////////////////
414 // tree popup autocomplete tests
415 queueAutoCompleteTests("autocomplete");
417 ////////////////////////////////////////////////////////////////////////////
418 // richlistbox popup autocomplete tests
419 queueAutoCompleteTests("richautocomplete");
421 ////////////////////////////////////////////////////////////////////////////
422 // HTML form autocomplete tests
423 queueAutoCompleteTests(getIFrameDOMDoc("iframe").getElementById("input"));
425 ////////////////////////////////////////////////////////////////////////////
426 // HTML5 list autocomplete tests
427 queueAutoCompleteTests(getIFrameDOMDoc("iframe2").getElementById("input"));
429 ////////////////////////////////////////////////////////////////////////////
430 // searchbar tests
432 // focus searchbar, focus on text entry
433 gQueue.push(new synthFocus("searchbar",
434 new focusChecker(getTextEntry, "searchbar")));
435 // open search engine popup, no focus
436 gQueue.push(new synthOpenComboboxKey("searchbar", new nofocusChecker()));
437 // select first item, focus on it
438 gQueue.push(new synthDownKey("searchbar",
439 new focusChecker(getItem, new itemObj("searchbar", 0))));
440 // mouse over on second item, focus on it
441 gQueue.push(new focusOnMouseOver(getItem, new itemObj("searchbar", 1)));
442 // press enter key, focus on text entry
443 gQueue.push(new synthEnterKey("searchbar",
444 new focusChecker(getTextEntry, "searchbar")));
445 // click on search button, open popup, focus goes to document
446 var searchBtn = getAccessible(getNode("searchbar").searchButton);
447 gQueue.push(new synthClick(searchBtn, new focusChecker(document)));
448 // select first item, focus on it
449 gQueue.push(new synthDownKey("searchbar",
450 new focusChecker(getItem, new itemObj("searchbar", 0))));
451 // close popup, focus goes on document
452 gQueue.push(new synthEscapeKey("searchbar", new focusChecker(document)));
454 gQueue.onFinish = function()
455 {
456 // unregister 'test-a11y-search' autocomplete search
457 shutdownAutoComplete();
458 }
459 gQueue.invoke(); // Will call SimpleTest.finish();
460 }
462 SimpleTest.waitForExplicitFinish();
464 // Register 'test-a11y-search' autocomplete search.
465 // XPFE AutoComplete needs to register early.
466 initAutoComplete([ "hello", "hi" ],
467 [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
469 addA11yLoadEvent(initTests);
470 ]]>
471 </script>
473 <hbox flex="1" style="overflow: auto;">
474 <body xmlns="http://www.w3.org/1999/xhtml">
475 <a target="_blank"
476 href="https://bugzilla.mozilla.org/show_bug.cgi?id=383759"
477 title="Focus event inconsistent for search box autocomplete">
478 Mozilla Bug 383759
479 </a>
480 <a target="_blank"
481 href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
482 title="Rework accessible focus handling">
483 Mozilla Bug 673958
484 </a>
485 <a target="_blank"
486 href="https://bugzilla.mozilla.org/show_bug.cgi?id=559766"
487 title="Add accessibility support for @list on HTML input and for HTML datalist">
488 Mozilla Bug 559766
489 </a>
490 <p id="display"></p>
491 <div id="content" style="display: none"></div>
492 <pre id="test">
493 </pre>
494 </body>
496 <vbox flex="1">
497 <textbox id="autocomplete" type="autocomplete"
498 autocompletesearch="test-a11y-search"/>
500 <textbox id="richautocomplete" type="autocomplete"
501 autocompletesearch="test-a11y-search"
502 autocompletepopup="richpopup"/>
503 <panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
505 <iframe id="iframe"/>
507 <iframe id="iframe2"/>
509 <searchbar id="searchbar"/>
511 <vbox id="eventdump"/>
512 </vbox>
513 </hbox>
514 </window>