test-firefox-tabs.js 30 KB

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