|
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 'use strict'; |
|
5 |
|
6 const { Cc, Ci } = require('chrome'); |
|
7 const { Loader } = require('sdk/test/loader'); |
|
8 const { setTimeout } = require('sdk/timers'); |
|
9 const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); |
|
10 const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils'); |
|
11 const { open, focus, close } = require('sdk/window/helpers'); |
|
12 const tabs = require('sdk/tabs'); |
|
13 const { browserWindows } = require('sdk/windows'); |
|
14 const { set: setPref } = require("sdk/preferences/service"); |
|
15 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; |
|
16 const fixtures = require("../fixtures"); |
|
17 |
|
18 // Bug 682681 - tab.title should never be empty |
|
19 exports.testBug682681_aboutURI = function(assert, done) { |
|
20 let url = 'chrome://browser/locale/tabbrowser.properties'; |
|
21 let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. |
|
22 getService(Ci.nsIStringBundleService). |
|
23 createBundle(url); |
|
24 let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle'); |
|
25 |
|
26 tabs.on('ready', function onReady(tab) { |
|
27 tabs.removeListener('ready', onReady); |
|
28 |
|
29 assert.equal(tab.title, |
|
30 emptyTabTitle, |
|
31 "title of about: tab is not blank"); |
|
32 |
|
33 tab.close(done); |
|
34 }); |
|
35 |
|
36 // open a about: url |
|
37 tabs.open({ |
|
38 url: "about:blank", |
|
39 inBackground: true |
|
40 }); |
|
41 }; |
|
42 |
|
43 // related to Bug 682681 |
|
44 exports.testTitleForDataURI = function(assert, done) { |
|
45 tabs.open({ |
|
46 url: "data:text/html;charset=utf-8,<title>tab</title>", |
|
47 inBackground: true, |
|
48 onReady: function(tab) { |
|
49 assert.equal(tab.title, "tab", "data: title is not Connecting..."); |
|
50 tab.close(done); |
|
51 } |
|
52 }); |
|
53 }; |
|
54 |
|
55 // TEST: 'BrowserWindow' instance creation on tab 'activate' event |
|
56 // See bug 648244: there was a infinite loop. |
|
57 exports.testBrowserWindowCreationOnActivate = function(assert, done) { |
|
58 let windows = require("sdk/windows").browserWindows; |
|
59 let gotActivate = false; |
|
60 |
|
61 tabs.once('activate', function onActivate(eventTab) { |
|
62 assert.ok(windows.activeWindow, "Is able to fetch activeWindow"); |
|
63 gotActivate = true; |
|
64 }); |
|
65 |
|
66 open().then(function(window) { |
|
67 assert.ok(gotActivate, "Received activate event"); |
|
68 return close(window); |
|
69 }).then(done).then(null, assert.fail); |
|
70 } |
|
71 |
|
72 // TEST: tab unloader |
|
73 exports.testAutomaticDestroyEventOpen = function(assert, done) { |
|
74 let called = false; |
|
75 let loader = Loader(module); |
|
76 let tabs2 = loader.require("sdk/tabs"); |
|
77 tabs2.on('open', _ => called = true); |
|
78 |
|
79 // Fire a tab event and ensure that the destroyed tab is inactive |
|
80 tabs.once('open', tab => { |
|
81 setTimeout(_ => { |
|
82 assert.ok(!called, "Unloaded tab module is destroyed and inactive"); |
|
83 tab.close(done); |
|
84 }); |
|
85 }); |
|
86 |
|
87 loader.unload(); |
|
88 tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventOpen"); |
|
89 }; |
|
90 |
|
91 exports.testAutomaticDestroyEventActivate = function(assert, done) { |
|
92 let called = false; |
|
93 let loader = Loader(module); |
|
94 let tabs2 = loader.require("sdk/tabs"); |
|
95 tabs2.on('activate', _ => called = true); |
|
96 |
|
97 // Fire a tab event and ensure that the destroyed tab is inactive |
|
98 tabs.once('activate', tab => { |
|
99 setTimeout(_ => { |
|
100 assert.ok(!called, "Unloaded tab module is destroyed and inactive"); |
|
101 tab.close(done); |
|
102 }); |
|
103 }); |
|
104 |
|
105 loader.unload(); |
|
106 tabs.open("data:text/html;charset=utf-8,testAutomaticDestroyEventActivate"); |
|
107 }; |
|
108 |
|
109 exports.testAutomaticDestroyEventDeactivate = function(assert, done) { |
|
110 let called = false; |
|
111 let currentTab = tabs.activeTab; |
|
112 let loader = Loader(module); |
|
113 let tabs2 = loader.require("sdk/tabs"); |
|
114 |
|
115 tabs.open({ |
|
116 url: "data:text/html;charset=utf-8,testAutomaticDestroyEventDeactivate", |
|
117 onActivate: _ => setTimeout(_ => { |
|
118 tabs2.on('deactivate', _ => called = true); |
|
119 |
|
120 // Fire a tab event and ensure that the destroyed tab is inactive |
|
121 tabs.once('deactivate', tab => { |
|
122 setTimeout(_ => { |
|
123 assert.ok(!called, "Unloaded tab module is destroyed and inactive"); |
|
124 tab.close(done); |
|
125 }); |
|
126 }); |
|
127 |
|
128 loader.unload(); |
|
129 currentTab.activate(); |
|
130 }) |
|
131 }); |
|
132 }; |
|
133 |
|
134 exports.testAutomaticDestroyEventClose = function(assert, done) { |
|
135 let called = false; |
|
136 let loader = Loader(module); |
|
137 let tabs2 = loader.require("sdk/tabs"); |
|
138 |
|
139 tabs.open({ |
|
140 url: "data:text/html;charset=utf-8,testAutomaticDestroyEventClose", |
|
141 onReady: tab => { |
|
142 tabs2.on('close', _ => called = true); |
|
143 |
|
144 // Fire a tab event and ensure that the destroyed tab is inactive |
|
145 tabs.once('close', tab => { |
|
146 setTimeout(_ => { |
|
147 assert.ok(!called, "Unloaded tab module is destroyed and inactive"); |
|
148 done(); |
|
149 }); |
|
150 }); |
|
151 |
|
152 loader.unload(); |
|
153 tab.close(); |
|
154 } |
|
155 }); |
|
156 }; |
|
157 |
|
158 exports.testTabPropertiesInNewWindow = function(assert, done) { |
|
159 let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" |
|
160 const { LoaderWithFilteredConsole } = require("sdk/test/loader"); |
|
161 let loader = LoaderWithFilteredConsole(module, function(type, message) { |
|
162 if (type == "error" && message.substring(0, warning.length) == warning) |
|
163 return false; |
|
164 return true; |
|
165 }); |
|
166 |
|
167 let tabs = loader.require('sdk/tabs'); |
|
168 let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils'); |
|
169 |
|
170 let count = 0; |
|
171 function onReadyOrLoad (tab) { |
|
172 if (count++) { |
|
173 close(getOwnerWindow(tab)).then(done).then(null, assert.fail); |
|
174 } |
|
175 } |
|
176 |
|
177 let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; |
|
178 tabs.open({ |
|
179 inNewWindow: true, |
|
180 url: url, |
|
181 onReady: function(tab) { |
|
182 assert.equal(tab.title, "foo", "title of the new tab matches"); |
|
183 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
184 assert.ok(tab.favicon, "favicon of the new tab is not empty"); |
|
185 assert.equal(tab.style, null, "style of the new tab matches"); |
|
186 assert.equal(tab.index, 0, "index of the new tab matches"); |
|
187 assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); |
|
188 assert.notEqual(tab.id, null, "a tab object always has an id property."); |
|
189 |
|
190 onReadyOrLoad(tab); |
|
191 }, |
|
192 onLoad: function(tab) { |
|
193 assert.equal(tab.title, "foo", "title of the new tab matches"); |
|
194 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
195 assert.ok(tab.favicon, "favicon of the new tab is not empty"); |
|
196 assert.equal(tab.style, null, "style of the new tab matches"); |
|
197 assert.equal(tab.index, 0, "index of the new tab matches"); |
|
198 assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); |
|
199 assert.notEqual(tab.id, null, "a tab object always has an id property."); |
|
200 |
|
201 onReadyOrLoad(tab); |
|
202 } |
|
203 }); |
|
204 }; |
|
205 |
|
206 exports.testTabPropertiesInSameWindow = function(assert, done) { |
|
207 let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" |
|
208 const { LoaderWithFilteredConsole } = require("sdk/test/loader"); |
|
209 let loader = LoaderWithFilteredConsole(module, function(type, message) { |
|
210 if (type == "error" && message.substring(0, warning.length) == warning) |
|
211 return false; |
|
212 return true; |
|
213 }); |
|
214 |
|
215 let tabs = loader.require('sdk/tabs'); |
|
216 |
|
217 // Get current count of tabs so we know the index of the |
|
218 // new tab, bug 893846 |
|
219 let tabCount = tabs.length; |
|
220 let count = 0; |
|
221 function onReadyOrLoad (tab) { |
|
222 if (count++) { |
|
223 tab.close(done); |
|
224 } |
|
225 } |
|
226 |
|
227 let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; |
|
228 tabs.open({ |
|
229 url: url, |
|
230 onReady: function(tab) { |
|
231 assert.equal(tab.title, "foo", "title of the new tab matches"); |
|
232 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
233 assert.ok(tab.favicon, "favicon of the new tab is not empty"); |
|
234 assert.equal(tab.style, null, "style of the new tab matches"); |
|
235 assert.equal(tab.index, tabCount, "index of the new tab matches"); |
|
236 assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); |
|
237 assert.notEqual(tab.id, null, "a tab object always has an id property."); |
|
238 |
|
239 onReadyOrLoad(tab); |
|
240 }, |
|
241 onLoad: function(tab) { |
|
242 assert.equal(tab.title, "foo", "title of the new tab matches"); |
|
243 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
244 assert.ok(tab.favicon, "favicon of the new tab is not empty"); |
|
245 assert.equal(tab.style, null, "style of the new tab matches"); |
|
246 assert.equal(tab.index, tabCount, "index of the new tab matches"); |
|
247 assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); |
|
248 assert.notEqual(tab.id, null, "a tab object always has an id property."); |
|
249 |
|
250 onReadyOrLoad(tab); |
|
251 } |
|
252 }); |
|
253 }; |
|
254 |
|
255 // TEST: tab properties |
|
256 exports.testTabContentTypeAndReload = function(assert, done) { |
|
257 open().then(focus).then(function(window) { |
|
258 let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; |
|
259 let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>"; |
|
260 tabs.open({ |
|
261 url: url, |
|
262 onReady: function(tab) { |
|
263 if (tab.url === url) { |
|
264 assert.equal(tab.contentType, "text/html"); |
|
265 tab.url = urlXML; |
|
266 } |
|
267 else { |
|
268 assert.equal(tab.contentType, "text/xml"); |
|
269 close(window).then(done).then(null, assert.fail); |
|
270 } |
|
271 } |
|
272 }); |
|
273 }); |
|
274 }; |
|
275 |
|
276 // TEST: tabs iterator and length property |
|
277 exports.testTabsIteratorAndLength = function(assert, done) { |
|
278 open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) { |
|
279 let startCount = 0; |
|
280 for each (let t in tabs) startCount++; |
|
281 assert.equal(startCount, tabs.length, "length property is correct"); |
|
282 let url = "data:text/html;charset=utf-8,default"; |
|
283 |
|
284 tabs.open(url); |
|
285 tabs.open(url); |
|
286 tabs.open({ |
|
287 url: url, |
|
288 onOpen: function(tab) { |
|
289 let count = 0; |
|
290 for each (let t in tabs) count++; |
|
291 assert.equal(count, startCount + 3, "iterated tab count matches"); |
|
292 assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); |
|
293 |
|
294 close(window).then(done).then(null, assert.fail); |
|
295 } |
|
296 }); |
|
297 }); |
|
298 }; |
|
299 |
|
300 // TEST: tab.url setter |
|
301 exports.testTabLocation = function(assert, done) { |
|
302 open().then(focus).then(function(window) { |
|
303 let url1 = "data:text/html;charset=utf-8,foo"; |
|
304 let url2 = "data:text/html;charset=utf-8,bar"; |
|
305 |
|
306 tabs.on('ready', function onReady(tab) { |
|
307 if (tab.url != url2) |
|
308 return; |
|
309 tabs.removeListener('ready', onReady); |
|
310 assert.pass("tab.load() loaded the correct url"); |
|
311 close(window).then(done).then(null, assert.fail); |
|
312 }); |
|
313 |
|
314 tabs.open({ |
|
315 url: url1, |
|
316 onOpen: function(tab) { |
|
317 tab.url = url2 |
|
318 } |
|
319 }); |
|
320 }); |
|
321 }; |
|
322 |
|
323 // TEST: tab.close() |
|
324 exports.testTabClose = function(assert, done) { |
|
325 let testName = "testTabClose"; |
|
326 let url = "data:text/html;charset=utf-8," + testName; |
|
327 |
|
328 assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); |
|
329 tabs.once('ready', function onReady(tab) { |
|
330 assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab"); |
|
331 assert.equal(url, tab.url, "tab url is the test url"); |
|
332 let secondOnCloseCalled = false; |
|
333 |
|
334 // Bug 699450: Multiple calls to tab.close should not throw |
|
335 tab.close(function() secondOnCloseCalled = true); |
|
336 try { |
|
337 tab.close(function () { |
|
338 assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); |
|
339 assert.ok(secondOnCloseCalled, |
|
340 "The immediate second call to tab.close happened"); |
|
341 assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); |
|
342 |
|
343 done(); |
|
344 }); |
|
345 } |
|
346 catch(e) { |
|
347 assert.fail("second call to tab.close() thrown an exception: " + e); |
|
348 } |
|
349 }); |
|
350 |
|
351 tabs.open(url); |
|
352 }; |
|
353 |
|
354 // TEST: tab.move() |
|
355 exports.testTabMove = function(assert, done) { |
|
356 open().then(focus).then(function(window) { |
|
357 let url = "data:text/html;charset=utf-8,foo"; |
|
358 |
|
359 tabs.open({ |
|
360 url: url, |
|
361 onOpen: function(tab) { |
|
362 assert.equal(tab.index, 1, "tab index before move matches"); |
|
363 tab.index = 0; |
|
364 assert.equal(tab.index, 0, "tab index after move matches"); |
|
365 close(window).then(done).then(null, assert.fail); |
|
366 } |
|
367 }); |
|
368 }).then(null, assert.fail); |
|
369 }; |
|
370 |
|
371 // TEST: open tab with default options |
|
372 exports.testOpen = function(assert, done) { |
|
373 let url = "data:text/html;charset=utf-8,default"; |
|
374 tabs.open({ |
|
375 url: url, |
|
376 onReady: function(tab) { |
|
377 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
378 assert.equal(tab.isPinned, false, "The new tab is not pinned"); |
|
379 |
|
380 tab.close(done); |
|
381 } |
|
382 }); |
|
383 }; |
|
384 |
|
385 // TEST: opening a pinned tab |
|
386 exports.testOpenPinned = function(assert, done) { |
|
387 let url = "data:text/html;charset=utf-8,default"; |
|
388 tabs.open({ |
|
389 url: url, |
|
390 isPinned: true, |
|
391 onOpen: function(tab) { |
|
392 assert.equal(tab.isPinned, true, "The new tab is pinned"); |
|
393 tab.close(done); |
|
394 } |
|
395 }); |
|
396 }; |
|
397 |
|
398 // TEST: pin/unpin opened tab |
|
399 exports.testPinUnpin = function(assert, done) { |
|
400 let url = "data:text/html;charset=utf-8,default"; |
|
401 tabs.open({ |
|
402 url: url, |
|
403 inBackground: true, |
|
404 onOpen: function(tab) { |
|
405 tab.pin(); |
|
406 assert.equal(tab.isPinned, true, "The tab was pinned correctly"); |
|
407 tab.unpin(); |
|
408 assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); |
|
409 tab.close(done); |
|
410 } |
|
411 }); |
|
412 } |
|
413 |
|
414 // TEST: open tab in background |
|
415 exports.testInBackground = function(assert, done) { |
|
416 let window = getMostRecentBrowserWindow(); |
|
417 let activeUrl = tabs.activeTab.url; |
|
418 let url = "data:text/html;charset=utf-8,background"; |
|
419 assert.equal(getMostRecentBrowserWindow(), window, "getMostRecentBrowserWindow() matches this window"); |
|
420 tabs.on('ready', function onReady(tab) { |
|
421 tabs.removeListener('ready', onReady); |
|
422 assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); |
|
423 assert.equal(tab.url, url, "URL of the new background tab matches"); |
|
424 assert.equal(getMostRecentBrowserWindow(), window, "a new window was not opened"); |
|
425 assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); |
|
426 tab.close(done); |
|
427 }); |
|
428 |
|
429 tabs.open({ |
|
430 url: url, |
|
431 inBackground: true |
|
432 }); |
|
433 } |
|
434 |
|
435 // TEST: open tab in new window |
|
436 exports.testOpenInNewWindow = function(assert, done) { |
|
437 let startWindowCount = windows().length; |
|
438 |
|
439 let url = "data:text/html;charset=utf-8,testOpenInNewWindow"; |
|
440 tabs.open({ |
|
441 url: url, |
|
442 inNewWindow: true, |
|
443 onReady: function(tab) { |
|
444 let newWindow = getOwnerWindow(tab); |
|
445 assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); |
|
446 |
|
447 onFocus(newWindow).then(function() { |
|
448 assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); |
|
449 assert.equal(tab.url, url, "URL of the new tab matches"); |
|
450 assert.equal(newWindow.content.location, url, "URL of new tab in new window matches"); |
|
451 assert.equal(tabs.activeTab.url, url, "URL of activeTab matches"); |
|
452 |
|
453 return close(newWindow).then(done); |
|
454 }).then(null, assert.fail); |
|
455 } |
|
456 }); |
|
457 |
|
458 } |
|
459 |
|
460 // Test tab.open inNewWindow + onOpen combination |
|
461 exports.testOpenInNewWindowOnOpen = function(assert, done) { |
|
462 let startWindowCount = windows().length; |
|
463 |
|
464 let url = "data:text/html;charset=utf-8,newwindow"; |
|
465 tabs.open({ |
|
466 url: url, |
|
467 inNewWindow: true, |
|
468 onOpen: function(tab) { |
|
469 let newWindow = getOwnerWindow(tab); |
|
470 |
|
471 onFocus(newWindow).then(function() { |
|
472 assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); |
|
473 assert.equal(getMostRecentBrowserWindow(), newWindow, "new window is active"); |
|
474 |
|
475 close(newWindow).then(done).then(null, assert.fail); |
|
476 }); |
|
477 } |
|
478 }); |
|
479 }; |
|
480 |
|
481 // TEST: onOpen event handler |
|
482 exports.testTabsEvent_onOpen = function(assert, done) { |
|
483 open().then(focus).then(window => { |
|
484 let url = "data:text/html;charset=utf-8,1"; |
|
485 let eventCount = 0; |
|
486 |
|
487 // add listener via property assignment |
|
488 function listener1(tab) { |
|
489 eventCount++; |
|
490 }; |
|
491 tabs.on('open', listener1); |
|
492 |
|
493 // add listener via collection add |
|
494 tabs.on('open', function listener2(tab) { |
|
495 assert.equal(++eventCount, 2, "both listeners notified"); |
|
496 tabs.removeListener('open', listener1); |
|
497 tabs.removeListener('open', listener2); |
|
498 close(window).then(done).then(null, assert.fail); |
|
499 }); |
|
500 |
|
501 tabs.open(url); |
|
502 }).then(null, assert.fail); |
|
503 }; |
|
504 |
|
505 // TEST: onClose event handler |
|
506 exports.testTabsEvent_onClose = function(assert, done) { |
|
507 open().then(focus).then(window => { |
|
508 let url = "data:text/html;charset=utf-8,onclose"; |
|
509 let eventCount = 0; |
|
510 |
|
511 // add listener via property assignment |
|
512 function listener1(tab) { |
|
513 eventCount++; |
|
514 } |
|
515 tabs.on('close', listener1); |
|
516 |
|
517 // add listener via collection add |
|
518 tabs.on('close', function listener2(tab) { |
|
519 assert.equal(++eventCount, 2, "both listeners notified"); |
|
520 tabs.removeListener('close', listener1); |
|
521 tabs.removeListener('close', listener2); |
|
522 close(window).then(done).then(null, assert.fail); |
|
523 }); |
|
524 |
|
525 tabs.on('ready', function onReady(tab) { |
|
526 tabs.removeListener('ready', onReady); |
|
527 tab.close(); |
|
528 }); |
|
529 |
|
530 tabs.open(url); |
|
531 }).then(null, assert.fail); |
|
532 }; |
|
533 |
|
534 // TEST: onClose event handler when a window is closed |
|
535 exports.testTabsEvent_onCloseWindow = function(assert, done) { |
|
536 let closeCount = 0; |
|
537 let individualCloseCount = 0; |
|
538 |
|
539 open().then(focus).then(window => { |
|
540 assert.pass('opened a new window'); |
|
541 |
|
542 tabs.on("close", function listener() { |
|
543 if (++closeCount == 4) { |
|
544 tabs.removeListener("close", listener); |
|
545 } |
|
546 }); |
|
547 |
|
548 function endTest() { |
|
549 if (++individualCloseCount < 3) { |
|
550 assert.pass('tab closed ' + individualCloseCount); |
|
551 return; |
|
552 } |
|
553 |
|
554 assert.equal(closeCount, 4, "Correct number of close events received"); |
|
555 assert.equal(individualCloseCount, 3, |
|
556 "Each tab with an attached onClose listener received a close " + |
|
557 "event when the window was closed"); |
|
558 |
|
559 done(); |
|
560 } |
|
561 |
|
562 // One tab is already open with the window |
|
563 let openTabs = 1; |
|
564 function testCasePossiblyLoaded() { |
|
565 if (++openTabs == 4) { |
|
566 window.close(); |
|
567 } |
|
568 assert.pass('tab opened ' + openTabs); |
|
569 } |
|
570 |
|
571 tabs.open({ |
|
572 url: "data:text/html;charset=utf-8,tab2", |
|
573 onOpen: testCasePossiblyLoaded, |
|
574 onClose: endTest |
|
575 }); |
|
576 |
|
577 tabs.open({ |
|
578 url: "data:text/html;charset=utf-8,tab3", |
|
579 onOpen: testCasePossiblyLoaded, |
|
580 onClose: endTest |
|
581 }); |
|
582 |
|
583 tabs.open({ |
|
584 url: "data:text/html;charset=utf-8,tab4", |
|
585 onOpen: testCasePossiblyLoaded, |
|
586 onClose: endTest |
|
587 }); |
|
588 }).then(null, assert.fail); |
|
589 } |
|
590 |
|
591 // TEST: onReady event handler |
|
592 exports.testTabsEvent_onReady = function(assert, done) { |
|
593 open().then(focus).then(window => { |
|
594 let url = "data:text/html;charset=utf-8,onready"; |
|
595 let eventCount = 0; |
|
596 |
|
597 // add listener via property assignment |
|
598 function listener1(tab) { |
|
599 eventCount++; |
|
600 }; |
|
601 tabs.on('ready', listener1); |
|
602 |
|
603 // add listener via collection add |
|
604 tabs.on('ready', function listener2(tab) { |
|
605 assert.equal(++eventCount, 2, "both listeners notified"); |
|
606 tabs.removeListener('ready', listener1); |
|
607 tabs.removeListener('ready', listener2); |
|
608 close(window).then(done); |
|
609 }); |
|
610 |
|
611 tabs.open(url); |
|
612 }).then(null, assert.fail); |
|
613 }; |
|
614 |
|
615 // TEST: onActivate event handler |
|
616 exports.testTabsEvent_onActivate = function(assert, done) { |
|
617 open().then(focus).then(window => { |
|
618 let url = "data:text/html;charset=utf-8,onactivate"; |
|
619 let eventCount = 0; |
|
620 |
|
621 // add listener via property assignment |
|
622 function listener1(tab) { |
|
623 eventCount++; |
|
624 }; |
|
625 tabs.on('activate', listener1); |
|
626 |
|
627 // add listener via collection add |
|
628 tabs.on('activate', function listener2(tab) { |
|
629 assert.equal(++eventCount, 2, "both listeners notified"); |
|
630 tabs.removeListener('activate', listener1); |
|
631 tabs.removeListener('activate', listener2); |
|
632 close(window).then(done).then(null, assert.fail); |
|
633 }); |
|
634 |
|
635 tabs.open(url); |
|
636 }).then(null, assert.fail); |
|
637 }; |
|
638 |
|
639 // onDeactivate event handler |
|
640 exports.testTabsEvent_onDeactivate = function(assert, done) { |
|
641 open().then(focus).then(window => { |
|
642 let url = "data:text/html;charset=utf-8,ondeactivate"; |
|
643 let eventCount = 0; |
|
644 |
|
645 // add listener via property assignment |
|
646 function listener1(tab) { |
|
647 eventCount++; |
|
648 }; |
|
649 tabs.on('deactivate', listener1); |
|
650 |
|
651 // add listener via collection add |
|
652 tabs.on('deactivate', function listener2(tab) { |
|
653 assert.equal(++eventCount, 2, "both listeners notified"); |
|
654 tabs.removeListener('deactivate', listener1); |
|
655 tabs.removeListener('deactivate', listener2); |
|
656 close(window).then(done).then(null, assert.fail); |
|
657 }); |
|
658 |
|
659 tabs.on('open', function onOpen(tab) { |
|
660 tabs.removeListener('open', onOpen); |
|
661 tabs.open("data:text/html;charset=utf-8,foo"); |
|
662 }); |
|
663 |
|
664 tabs.open(url); |
|
665 }).then(null, assert.fail); |
|
666 }; |
|
667 |
|
668 // pinning |
|
669 exports.testTabsEvent_pinning = function(assert, done) { |
|
670 open().then(focus).then(window => { |
|
671 let url = "data:text/html;charset=utf-8,1"; |
|
672 |
|
673 tabs.on('open', function onOpen(tab) { |
|
674 tabs.removeListener('open', onOpen); |
|
675 tab.pin(); |
|
676 }); |
|
677 |
|
678 tabs.on('pinned', function onPinned(tab) { |
|
679 tabs.removeListener('pinned', onPinned); |
|
680 assert.ok(tab.isPinned, "notified tab is pinned"); |
|
681 tab.unpin(); |
|
682 }); |
|
683 |
|
684 tabs.on('unpinned', function onUnpinned(tab) { |
|
685 tabs.removeListener('unpinned', onUnpinned); |
|
686 assert.ok(!tab.isPinned, "notified tab is not pinned"); |
|
687 close(window).then(done).then(null, assert.fail); |
|
688 }); |
|
689 |
|
690 tabs.open(url); |
|
691 }).then(null, assert.fail); |
|
692 }; |
|
693 |
|
694 // TEST: per-tab event handlers |
|
695 exports.testPerTabEvents = function(assert, done) { |
|
696 open().then(focus).then(window => { |
|
697 let eventCount = 0; |
|
698 |
|
699 tabs.open({ |
|
700 url: "data:text/html;charset=utf-8,foo", |
|
701 onOpen: function(tab) { |
|
702 // add listener via property assignment |
|
703 function listener1() { |
|
704 eventCount++; |
|
705 }; |
|
706 tab.on('ready', listener1); |
|
707 |
|
708 // add listener via collection add |
|
709 tab.on('ready', function listener2() { |
|
710 assert.equal(eventCount, 1, "both listeners notified"); |
|
711 tab.removeListener('ready', listener1); |
|
712 tab.removeListener('ready', listener2); |
|
713 close(window).then(done).then(null, assert.fail); |
|
714 }); |
|
715 } |
|
716 }); |
|
717 }).then(null, assert.fail); |
|
718 }; |
|
719 |
|
720 exports.testAttachOnOpen = function (assert, done) { |
|
721 // Take care that attach has to be called on tab ready and not on tab open. |
|
722 open().then(focus).then(window => { |
|
723 tabs.open({ |
|
724 url: "data:text/html;charset=utf-8,foobar", |
|
725 onOpen: function (tab) { |
|
726 let worker = tab.attach({ |
|
727 contentScript: 'self.postMessage(document.location.href); ', |
|
728 onMessage: function (msg) { |
|
729 assert.equal(msg, "about:blank", |
|
730 "Worker document url is about:blank on open"); |
|
731 worker.destroy(); |
|
732 close(window).then(done).then(null, assert.fail); |
|
733 } |
|
734 }); |
|
735 } |
|
736 }); |
|
737 }).then(null, assert.fail); |
|
738 } |
|
739 |
|
740 exports.testAttachOnMultipleDocuments = function (assert, done) { |
|
741 // Example of attach that process multiple tab documents |
|
742 open().then(focus).then(window => { |
|
743 let firstLocation = "data:text/html;charset=utf-8,foobar"; |
|
744 let secondLocation = "data:text/html;charset=utf-8,bar"; |
|
745 let thirdLocation = "data:text/html;charset=utf-8,fox"; |
|
746 let onReadyCount = 0; |
|
747 let worker1 = null; |
|
748 let worker2 = null; |
|
749 let detachEventCount = 0; |
|
750 |
|
751 tabs.open({ |
|
752 url: firstLocation, |
|
753 onReady: function (tab) { |
|
754 onReadyCount++; |
|
755 if (onReadyCount == 1) { |
|
756 worker1 = tab.attach({ |
|
757 contentScript: 'self.on("message", ' + |
|
758 ' function () self.postMessage(document.location.href)' + |
|
759 ');', |
|
760 onMessage: function (msg) { |
|
761 assert.equal(msg, firstLocation, |
|
762 "Worker url is equal to the 1st document"); |
|
763 tab.url = secondLocation; |
|
764 }, |
|
765 onDetach: function () { |
|
766 detachEventCount++; |
|
767 assert.pass("Got worker1 detach event"); |
|
768 assert.throws(function () { |
|
769 worker1.postMessage("ex-1"); |
|
770 }, |
|
771 /Couldn't find the worker/, |
|
772 "postMessage throw because worker1 is destroyed"); |
|
773 checkEnd(); |
|
774 } |
|
775 }); |
|
776 worker1.postMessage("new-doc-1"); |
|
777 } |
|
778 else if (onReadyCount == 2) { |
|
779 |
|
780 worker2 = tab.attach({ |
|
781 contentScript: 'self.on("message", ' + |
|
782 ' function () self.postMessage(document.location.href)' + |
|
783 ');', |
|
784 onMessage: function (msg) { |
|
785 assert.equal(msg, secondLocation, |
|
786 "Worker url is equal to the 2nd document"); |
|
787 tab.url = thirdLocation; |
|
788 }, |
|
789 onDetach: function () { |
|
790 detachEventCount++; |
|
791 assert.pass("Got worker2 detach event"); |
|
792 assert.throws(function () { |
|
793 worker2.postMessage("ex-2"); |
|
794 }, |
|
795 /Couldn't find the worker/, |
|
796 "postMessage throw because worker2 is destroyed"); |
|
797 checkEnd(); |
|
798 } |
|
799 }); |
|
800 worker2.postMessage("new-doc-2"); |
|
801 } |
|
802 else if (onReadyCount == 3) { |
|
803 tab.close(); |
|
804 } |
|
805 } |
|
806 }); |
|
807 |
|
808 function checkEnd() { |
|
809 if (detachEventCount != 2) |
|
810 return; |
|
811 |
|
812 assert.pass("Got all detach events"); |
|
813 |
|
814 close(window).then(done).then(null, assert.fail); |
|
815 } |
|
816 }).then(null, assert.fail); |
|
817 } |
|
818 |
|
819 |
|
820 exports.testAttachWrappers = function (assert, done) { |
|
821 // Check that content script has access to wrapped values by default |
|
822 open().then(focus).then(window => { |
|
823 let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " + |
|
824 " document.getElementById = 3;</script>"; |
|
825 let count = 0; |
|
826 |
|
827 tabs.open({ |
|
828 url: document, |
|
829 onReady: function (tab) { |
|
830 let worker = tab.attach({ |
|
831 contentScript: 'try {' + |
|
832 ' self.postMessage(!("globalJSVar" in window));' + |
|
833 ' self.postMessage(typeof window.globalJSVar == "undefined");' + |
|
834 '} catch(e) {' + |
|
835 ' self.postMessage(e.message);' + |
|
836 '}', |
|
837 onMessage: function (msg) { |
|
838 assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); |
|
839 if (count++ == 1) |
|
840 close(window).then(done).then(null, assert.fail); |
|
841 } |
|
842 }); |
|
843 } |
|
844 }); |
|
845 }).then(null, assert.fail); |
|
846 } |
|
847 |
|
848 /* |
|
849 // We do not offer unwrapped access to DOM since bug 601295 landed |
|
850 // See 660780 to track progress of unwrap feature |
|
851 exports.testAttachUnwrapped = function (assert, done) { |
|
852 // Check that content script has access to unwrapped values through unsafeWindow |
|
853 openBrowserWindow(function(window, browser) { |
|
854 let document = "data:text/html;charset=utf-8,<script>var globalJSVar=true;</script>"; |
|
855 let count = 0; |
|
856 |
|
857 tabs.open({ |
|
858 url: document, |
|
859 onReady: function (tab) { |
|
860 let worker = tab.attach({ |
|
861 contentScript: 'try {' + |
|
862 ' self.postMessage(unsafeWindow.globalJSVar);' + |
|
863 '} catch(e) {' + |
|
864 ' self.postMessage(e.message);' + |
|
865 '}', |
|
866 onMessage: function (msg) { |
|
867 assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")"); |
|
868 close(window).then(done); |
|
869 } |
|
870 }); |
|
871 } |
|
872 }); |
|
873 |
|
874 }); |
|
875 } |
|
876 */ |
|
877 |
|
878 exports['test window focus changes active tab'] = function(assert, done) { |
|
879 let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1"); |
|
880 |
|
881 let win1 = openBrowserWindow(function() { |
|
882 assert.pass("window 1 is open"); |
|
883 |
|
884 let win2 = openBrowserWindow(function() { |
|
885 assert.pass("window 2 is open"); |
|
886 |
|
887 focus(win2).then(function() { |
|
888 tabs.on("activate", function onActivate(tab) { |
|
889 tabs.removeListener("activate", onActivate); |
|
890 assert.pass("activate was called on windows focus change."); |
|
891 assert.equal(tab.url, url1, 'the activated tab url is correct'); |
|
892 |
|
893 return close(win2).then(function() { |
|
894 assert.pass('window 2 was closed'); |
|
895 return close(win1); |
|
896 }).then(done).then(null, assert.fail); |
|
897 }); |
|
898 |
|
899 win1.focus(); |
|
900 }); |
|
901 }, "data:text/html;charset=utf-8,test window focus changes active tab</br><h1>Window #2"); |
|
902 }, url1); |
|
903 }; |
|
904 |
|
905 exports['test ready event on new window tab'] = function(assert, done) { |
|
906 let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!"); |
|
907 |
|
908 require("sdk/tabs").on("ready", function onReady(tab) { |
|
909 if (tab.url === uri) { |
|
910 require("sdk/tabs").removeListener("ready", onReady); |
|
911 assert.pass("ready event was emitted"); |
|
912 close(window).then(done).then(null, assert.fail); |
|
913 } |
|
914 }); |
|
915 |
|
916 let window = openBrowserWindow(function(){}, uri); |
|
917 }; |
|
918 |
|
919 exports['test unique tab ids'] = function(assert, done) { |
|
920 var windows = require('sdk/windows').browserWindows; |
|
921 var { all, defer } = require('sdk/core/promise'); |
|
922 |
|
923 function openWindow() { |
|
924 let deferred = defer(); |
|
925 let win = windows.open({ |
|
926 url: "data:text/html;charset=utf-8,<html>foo</html>", |
|
927 }); |
|
928 |
|
929 win.on('open', function(window) { |
|
930 assert.ok(window.tabs.length); |
|
931 assert.ok(window.tabs.activeTab); |
|
932 assert.ok(window.tabs.activeTab.id); |
|
933 deferred.resolve({ |
|
934 id: window.tabs.activeTab.id, |
|
935 win: win |
|
936 }); |
|
937 }); |
|
938 |
|
939 return deferred.promise; |
|
940 } |
|
941 |
|
942 var one = openWindow(), two = openWindow(); |
|
943 all([one, two]).then(function(results) { |
|
944 assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal."); |
|
945 results[0].win.close(); |
|
946 results[1].win.close(); |
|
947 done(); |
|
948 }); |
|
949 } |
|
950 |
|
951 // related to Bug 671305 |
|
952 exports.testOnLoadEventWithDOM = function(assert, done) { |
|
953 let count = 0; |
|
954 let title = 'testOnLoadEventWithDOM'; |
|
955 |
|
956 // open a about: url |
|
957 tabs.open({ |
|
958 url: 'data:text/html;charset=utf-8,<title>' + title + '</title>', |
|
959 inBackground: true, |
|
960 onLoad: function(tab) { |
|
961 assert.equal(tab.title, title, 'tab passed in as arg, load called'); |
|
962 |
|
963 if (++count > 1) { |
|
964 assert.pass('onLoad event called on reload'); |
|
965 tab.close(done); |
|
966 } |
|
967 else { |
|
968 assert.pass('first onLoad event occured'); |
|
969 tab.reload(); |
|
970 } |
|
971 } |
|
972 }); |
|
973 }; |
|
974 |
|
975 // related to Bug 671305 |
|
976 exports.testOnLoadEventWithImage = function(assert, done) { |
|
977 let count = 0; |
|
978 |
|
979 tabs.open({ |
|
980 url: fixtures.url('Firefox.jpg'), |
|
981 inBackground: true, |
|
982 onLoad: function(tab) { |
|
983 if (++count > 1) { |
|
984 assert.pass('onLoad event called on reload with image'); |
|
985 tab.close(done); |
|
986 } |
|
987 else { |
|
988 assert.pass('first onLoad event occured'); |
|
989 tab.reload(); |
|
990 } |
|
991 } |
|
992 }); |
|
993 }; |
|
994 |
|
995 exports.testFaviconGetterDeprecation = function (assert, done) { |
|
996 setPref(DEPRECATE_PREF, true); |
|
997 const { LoaderWithHookedConsole } = require("sdk/test/loader"); |
|
998 let { loader, messages } = LoaderWithHookedConsole(module); |
|
999 let tabs = loader.require('sdk/tabs'); |
|
1000 |
|
1001 tabs.open({ |
|
1002 url: 'data:text/html;charset=utf-8,', |
|
1003 onOpen: function (tab) { |
|
1004 let favicon = tab.favicon; |
|
1005 assert.ok(messages.length === 1, 'only one error is dispatched'); |
|
1006 assert.ok(messages[0].type, 'error', 'the console message is an error'); |
|
1007 |
|
1008 let msg = messages[0].msg; |
|
1009 assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1, |
|
1010 'message contains the given message'); |
|
1011 tab.close(done); |
|
1012 loader.unload(); |
|
1013 } |
|
1014 }); |
|
1015 } |
|
1016 |
|
1017 /******************* helpers *********************/ |
|
1018 |
|
1019 // Utility function to open a new browser window. |
|
1020 function openBrowserWindow(callback, url) { |
|
1021 let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. |
|
1022 getService(Ci.nsIWindowWatcher); |
|
1023 let urlString = Cc["@mozilla.org/supports-string;1"]. |
|
1024 createInstance(Ci.nsISupportsString); |
|
1025 urlString.data = url; |
|
1026 let window = ww.openWindow(null, "chrome://browser/content/browser.xul", |
|
1027 "_blank", "chrome,all,dialog=no", urlString); |
|
1028 |
|
1029 if (callback) { |
|
1030 window.addEventListener("load", function onLoad(event) { |
|
1031 if (event.target && event.target.defaultView == window) { |
|
1032 window.removeEventListener("load", onLoad, true); |
|
1033 let browsers = window.document.getElementsByTagName("tabbrowser"); |
|
1034 try { |
|
1035 setTimeout(function () { |
|
1036 callback(window, browsers[0]); |
|
1037 }, 10); |
|
1038 } |
|
1039 catch (e) { |
|
1040 console.exception(e); |
|
1041 } |
|
1042 } |
|
1043 }, true); |
|
1044 } |
|
1045 |
|
1046 return window; |
|
1047 } |
|
1048 |
|
1049 require('sdk/test').run(exports); |