|
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 /** |
|
6 * Test for bug 549340. |
|
7 * Test for browser.js::contentAreaClick() util. |
|
8 * |
|
9 * The test opens a new browser window, then replaces browser.js methods invoked |
|
10 * by contentAreaClick with a mock function that tracks which methods have been |
|
11 * called. |
|
12 * Each sub-test synthesizes a mouse click event on links injected in content, |
|
13 * the event is collected by a click handler that ensures that contentAreaClick |
|
14 * correctly prevent default events, and follows the correct code path. |
|
15 */ |
|
16 |
|
17 let gTests = [ |
|
18 |
|
19 { |
|
20 desc: "Simple left click", |
|
21 setup: function() {}, |
|
22 clean: function() {}, |
|
23 event: {}, |
|
24 targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ], |
|
25 expectedInvokedMethods: [], |
|
26 preventDefault: false, |
|
27 }, |
|
28 |
|
29 { |
|
30 desc: "Ctrl/Cmd left click", |
|
31 setup: function() {}, |
|
32 clean: function() {}, |
|
33 event: { ctrlKey: true, |
|
34 metaKey: true }, |
|
35 targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ], |
|
36 expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ], |
|
37 preventDefault: true, |
|
38 }, |
|
39 |
|
40 // The next test was once handling feedService.forcePreview(). Now it should |
|
41 // just be like Alt click. |
|
42 { |
|
43 desc: "Shift+Alt left click", |
|
44 setup: function() { |
|
45 gPrefService.setBoolPref("browser.altClickSave", true); |
|
46 }, |
|
47 clean: function() { |
|
48 gPrefService.clearUserPref("browser.altClickSave"); |
|
49 }, |
|
50 event: { shiftKey: true, |
|
51 altKey: true }, |
|
52 targets: [ "commonlink", "maplink" ], |
|
53 expectedInvokedMethods: [ "gatherTextUnder", "saveURL" ], |
|
54 preventDefault: true, |
|
55 }, |
|
56 |
|
57 { |
|
58 desc: "Shift+Alt left click on XLinks", |
|
59 setup: function() { |
|
60 gPrefService.setBoolPref("browser.altClickSave", true); |
|
61 }, |
|
62 clean: function() { |
|
63 gPrefService.clearUserPref("browser.altClickSave"); |
|
64 }, |
|
65 event: { shiftKey: true, |
|
66 altKey: true }, |
|
67 targets: [ "mathxlink", "svgxlink"], |
|
68 expectedInvokedMethods: [ "saveURL" ], |
|
69 preventDefault: true, |
|
70 }, |
|
71 |
|
72 { |
|
73 desc: "Shift click", |
|
74 setup: function() {}, |
|
75 clean: function() {}, |
|
76 event: { shiftKey: true }, |
|
77 targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ], |
|
78 expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ], |
|
79 preventDefault: true, |
|
80 }, |
|
81 |
|
82 { |
|
83 desc: "Alt click", |
|
84 setup: function() { |
|
85 gPrefService.setBoolPref("browser.altClickSave", true); |
|
86 }, |
|
87 clean: function() { |
|
88 gPrefService.clearUserPref("browser.altClickSave"); |
|
89 }, |
|
90 event: { altKey: true }, |
|
91 targets: [ "commonlink", "maplink" ], |
|
92 expectedInvokedMethods: [ "gatherTextUnder", "saveURL" ], |
|
93 preventDefault: true, |
|
94 }, |
|
95 |
|
96 { |
|
97 desc: "Alt click on XLinks", |
|
98 setup: function() { |
|
99 gPrefService.setBoolPref("browser.altClickSave", true); |
|
100 }, |
|
101 clean: function() { |
|
102 gPrefService.clearUserPref("browser.altClickSave"); |
|
103 }, |
|
104 event: { altKey: true }, |
|
105 targets: [ "mathxlink", "svgxlink" ], |
|
106 expectedInvokedMethods: [ "saveURL" ], |
|
107 preventDefault: true, |
|
108 }, |
|
109 |
|
110 { |
|
111 desc: "Panel click", |
|
112 setup: function() {}, |
|
113 clean: function() {}, |
|
114 event: {}, |
|
115 targets: [ "panellink" ], |
|
116 expectedInvokedMethods: [ "urlSecurityCheck", "loadURI" ], |
|
117 preventDefault: true, |
|
118 }, |
|
119 |
|
120 { |
|
121 desc: "Simple middle click opentab", |
|
122 setup: function() {}, |
|
123 clean: function() {}, |
|
124 event: { button: 1 }, |
|
125 targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ], |
|
126 expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ], |
|
127 preventDefault: true, |
|
128 }, |
|
129 |
|
130 { |
|
131 desc: "Simple middle click openwin", |
|
132 setup: function() { |
|
133 gPrefService.setBoolPref("browser.tabs.opentabfor.middleclick", false); |
|
134 }, |
|
135 clean: function() { |
|
136 gPrefService.clearUserPref("browser.tabs.opentabfor.middleclick"); |
|
137 }, |
|
138 event: { button: 1 }, |
|
139 targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ], |
|
140 expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ], |
|
141 preventDefault: true, |
|
142 }, |
|
143 |
|
144 { |
|
145 desc: "Middle mouse paste", |
|
146 setup: function() { |
|
147 gPrefService.setBoolPref("middlemouse.contentLoadURL", true); |
|
148 gPrefService.setBoolPref("general.autoScroll", false); |
|
149 }, |
|
150 clean: function() { |
|
151 gPrefService.clearUserPref("middlemouse.contentLoadURL"); |
|
152 gPrefService.clearUserPref("general.autoScroll"); |
|
153 }, |
|
154 event: { button: 1 }, |
|
155 targets: [ "emptylink" ], |
|
156 expectedInvokedMethods: [ "middleMousePaste" ], |
|
157 preventDefault: true, |
|
158 }, |
|
159 |
|
160 ]; |
|
161 |
|
162 // Array of method names that will be replaced in the new window. |
|
163 let gReplacedMethods = [ |
|
164 "middleMousePaste", |
|
165 "urlSecurityCheck", |
|
166 "loadURI", |
|
167 "gatherTextUnder", |
|
168 "saveURL", |
|
169 "openLinkIn", |
|
170 "getShortcutOrURIAndPostData", |
|
171 ]; |
|
172 |
|
173 // Reference to the new window. |
|
174 let gTestWin = null; |
|
175 |
|
176 // List of methods invoked by a specific call to contentAreaClick. |
|
177 let gInvokedMethods = []; |
|
178 |
|
179 // The test currently running. |
|
180 let gCurrentTest = null; |
|
181 |
|
182 function test() { |
|
183 waitForExplicitFinish(); |
|
184 |
|
185 gTestWin = openDialog(location, "", "chrome,all,dialog=no", "about:blank"); |
|
186 whenDelayedStartupFinished(gTestWin, function () { |
|
187 info("Browser window opened"); |
|
188 waitForFocus(function() { |
|
189 info("Browser window focused"); |
|
190 waitForFocus(function() { |
|
191 info("Setting up browser..."); |
|
192 setupTestBrowserWindow(); |
|
193 info("Running tests..."); |
|
194 executeSoon(runNextTest); |
|
195 }, gTestWin.content, true); |
|
196 }, gTestWin); |
|
197 }); |
|
198 } |
|
199 |
|
200 // Click handler used to steal click events. |
|
201 let gClickHandler = { |
|
202 handleEvent: function (event) { |
|
203 let linkId = event.target.id || event.target.localName; |
|
204 is(event.type, "click", |
|
205 gCurrentTest.desc + ":Handler received a click event on " + linkId); |
|
206 |
|
207 let isPanelClick = linkId == "panellink"; |
|
208 gTestWin.contentAreaClick(event, isPanelClick); |
|
209 let prevent = event.defaultPrevented; |
|
210 is(prevent, gCurrentTest.preventDefault, |
|
211 gCurrentTest.desc + ": event.defaultPrevented is correct (" + prevent + ")") |
|
212 |
|
213 // Check that all required methods have been called. |
|
214 gCurrentTest.expectedInvokedMethods.forEach(function(aExpectedMethodName) { |
|
215 isnot(gInvokedMethods.indexOf(aExpectedMethodName), -1, |
|
216 gCurrentTest.desc + ":" + aExpectedMethodName + " was invoked"); |
|
217 }); |
|
218 |
|
219 if (gInvokedMethods.length != gCurrentTest.expectedInvokedMethods.length) { |
|
220 ok(false, "Wrong number of invoked methods"); |
|
221 gInvokedMethods.forEach(function (method) info(method + " was invoked")); |
|
222 } |
|
223 |
|
224 event.preventDefault(); |
|
225 event.stopPropagation(); |
|
226 |
|
227 executeSoon(runNextTest); |
|
228 } |
|
229 } |
|
230 |
|
231 // Wraps around the methods' replacement mock function. |
|
232 function wrapperMethod(aInvokedMethods, aMethodName) { |
|
233 return function () { |
|
234 aInvokedMethods.push(aMethodName); |
|
235 // At least getShortcutOrURIAndPostData requires to return url |
|
236 return (aMethodName == "getShortcutOrURIAndPostData") ? arguments.url : arguments[0]; |
|
237 } |
|
238 } |
|
239 |
|
240 function setupTestBrowserWindow() { |
|
241 // Steal click events and don't propagate them. |
|
242 gTestWin.addEventListener("click", gClickHandler, true); |
|
243 |
|
244 // Replace methods. |
|
245 gReplacedMethods.forEach(function (aMethodName) { |
|
246 gTestWin["old_" + aMethodName] = gTestWin[aMethodName]; |
|
247 gTestWin[aMethodName] = wrapperMethod(gInvokedMethods, aMethodName); |
|
248 }); |
|
249 |
|
250 // Inject links in content. |
|
251 let doc = gTestWin.content.document; |
|
252 let mainDiv = doc.createElement("div"); |
|
253 mainDiv.innerHTML = |
|
254 '<p><a id="commonlink" href="http://mochi.test/moz/">Common link</a></p>' + |
|
255 '<p><a id="panellink" href="http://mochi.test/moz/">Panel link</a></p>' + |
|
256 '<p><a id="emptylink">Empty link</a></p>' + |
|
257 '<p><math id="mathxlink" xmlns="http://www.w3.org/1998/Math/MathML" xlink:type="simple" xlink:href="http://mochi.test/moz/"><mtext>MathML XLink</mtext></math></p>' + |
|
258 '<p><svg id="svgxlink" xmlns="http://www.w3.org/2000/svg" width="100px" height="50px" version="1.1"><a xlink:type="simple" xlink:href="http://mochi.test/moz/"><text transform="translate(10, 25)">SVG XLink</text></a></svg></p>' + |
|
259 '<p><map name="map" id="map"><area href="http://mochi.test/moz/" shape="rect" coords="0,0,128,128" /></map><img id="maplink" usemap="#map" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAAABGdBTUEAALGPC%2FxhBQAAAOtJREFUeF7t0IEAAAAAgKD9qRcphAoDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGBgwIAAAT0N51AAAAAASUVORK5CYII%3D"/></p>' |
|
260 doc.body.appendChild(mainDiv); |
|
261 } |
|
262 |
|
263 function runNextTest() { |
|
264 if (!gCurrentTest) { |
|
265 gCurrentTest = gTests.shift(); |
|
266 gCurrentTest.setup(); |
|
267 } |
|
268 |
|
269 if (gCurrentTest.targets.length == 0) { |
|
270 info(gCurrentTest.desc + ": cleaning up...") |
|
271 gCurrentTest.clean(); |
|
272 |
|
273 if (gTests.length > 0) { |
|
274 gCurrentTest = gTests.shift(); |
|
275 gCurrentTest.setup(); |
|
276 } |
|
277 else { |
|
278 finishTest(); |
|
279 return; |
|
280 } |
|
281 } |
|
282 |
|
283 // Move to next target. |
|
284 gInvokedMethods.length = 0; |
|
285 let target = gCurrentTest.targets.shift(); |
|
286 |
|
287 info(gCurrentTest.desc + ": testing " + target); |
|
288 |
|
289 // Fire click event. |
|
290 let targetElt = gTestWin.content.document.getElementById(target); |
|
291 ok(targetElt, gCurrentTest.desc + ": target is valid (" + targetElt.id + ")"); |
|
292 EventUtils.synthesizeMouseAtCenter(targetElt, gCurrentTest.event, gTestWin.content); |
|
293 } |
|
294 |
|
295 function finishTest() { |
|
296 info("Restoring browser..."); |
|
297 gTestWin.removeEventListener("click", gClickHandler, true); |
|
298 |
|
299 // Restore original methods. |
|
300 gReplacedMethods.forEach(function (aMethodName) { |
|
301 gTestWin[aMethodName] = gTestWin["old_" + aMethodName]; |
|
302 delete gTestWin["old_" + aMethodName]; |
|
303 }); |
|
304 |
|
305 gTestWin.close(); |
|
306 finish(); |
|
307 } |