Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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';
6 const { Cc, Ci } = require('chrome');
7 const { setTimeout } = require('sdk/timers');
8 const { Loader } = require('sdk/test/loader');
9 const { onFocus, getMostRecentWindow, windows, isBrowser, getWindowTitle } = require('sdk/window/utils');
10 const { open, close, focus } = require('sdk/window/helpers');
11 const { browserWindows } = require("sdk/windows");
12 const tabs = require("sdk/tabs");
13 const winUtils = require("sdk/deprecated/window-utils");
14 const { WindowTracker } = winUtils;
15 const { isPrivate } = require('sdk/private-browsing');
16 const { isWindowPBSupported } = require('sdk/private-browsing/utils');
17 const { viewFor } = require("sdk/view/core");
18 const { defer } = require("sdk/lang/functional");
20 // TEST: open & close window
21 exports.testOpenAndCloseWindow = function(assert, done) {
22 assert.equal(browserWindows.length, 1, "Only one window open");
23 let title = 'testOpenAndCloseWindow';
25 browserWindows.open({
26 url: "data:text/html;charset=utf-8,<title>" + title + "</title>",
27 onOpen: function(window) {
28 assert.equal(this, browserWindows, "The 'this' object is the windows object.");
29 assert.equal(window.tabs.length, 1, "Only one tab open");
30 assert.equal(browserWindows.length, 2, "Two windows open");
32 window.tabs.activeTab.once('ready', function onReady(tab) {
33 assert.pass(RegExp(title).test(window.title), "URL correctly loaded");
34 window.close();
35 });
36 },
37 onClose: function(window) {
38 assert.equal(window.tabs.length, 0, "Tabs were cleared");
39 assert.equal(browserWindows.length, 1, "Only one window open");
40 done();
41 }
42 });
43 };
45 exports.testAutomaticDestroy = function(assert, done) {
46 let windows = browserWindows;
48 // Create a second windows instance that we will unload
49 let called = false;
50 let loader = Loader(module);
51 let windows2 = loader.require("sdk/windows").browserWindows;
53 windows2.on("open", function() {
54 called = true;
55 });
57 loader.unload();
59 // Fire a windows event and check that this unloaded instance is inactive
60 windows.open({
61 url: "data:text/html;charset=utf-8,foo",
62 onOpen: function(window) {
63 setTimeout(function () {
64 assert.ok(!called, "Unloaded windows instance is destroyed and inactive");
66 window.close(done);
67 });
68 }
69 });
70 };
72 exports.testWindowTabsObject = function(assert, done) {
73 let window, count = 0;
74 function runTest() {
75 if (++count != 2)
76 return;
78 assert.equal(window.tabs.length, 1, "Only 1 tab open");
79 assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");
81 window.tabs.open({
82 url: "data:text/html;charset=utf-8,<title>tab 2</title>",
83 inBackground: true,
84 onReady: function onReady(newTab) {
85 assert.equal(window.tabs.length, 2, "New tab open");
86 assert.equal(newTab.title, "tab 2", "Correct new tab title");
87 assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");
89 let i = 1;
90 for (let tab of window.tabs)
91 assert.equal(tab.title, "tab " + i++, "Correct title");
93 window.close();
94 }
95 });
96 }
98 tabs.once("ready", runTest);
100 browserWindows.open({
101 url: "data:text/html;charset=utf-8,<title>tab 1</title>",
102 onActivate: function onActivate(win) {
103 window = win;
104 runTest();
105 },
106 onClose: function onClose(window) {
107 assert.equal(window.tabs.length, 0, "No more tabs on closed window");
108 done();
109 }
110 });
111 };
113 exports.testOnOpenOnCloseListeners = function(assert, done) {
114 let windows = browserWindows;
116 assert.equal(browserWindows.length, 1, "Only one window open");
118 let received = {
119 listener1: false,
120 listener2: false,
121 listener3: false,
122 listener4: false
123 }
125 function listener1() {
126 assert.equal(this, windows, "The 'this' object is the windows object.");
128 if (received.listener1)
129 assert.fail("Event received twice");
130 received.listener1 = true;
131 }
133 function listener2() {
134 if (received.listener2)
135 assert.fail("Event received twice");
136 received.listener2 = true;
137 }
139 function listener3() {
140 assert.equal(this, windows, "The 'this' object is the windows object.");
141 if (received.listener3)
142 assert.fail("Event received twice");
143 received.listener3 = true;
144 }
146 function listener4() {
147 if (received.listener4)
148 assert.fail("Event received twice");
149 received.listener4 = true;
150 }
152 windows.on('open', listener1);
153 windows.on('open', listener2);
154 windows.on('close', listener3);
155 windows.on('close', listener4);
157 windows.open({
158 url: "data:text/html;charset=utf-8,foo",
159 onOpen: function(window) {
160 window.close(function() {
161 assert.ok(received.listener1, "onOpen handler called");
162 assert.ok(received.listener2, "onOpen handler called");
163 assert.ok(received.listener3, "onClose handler called");
164 assert.ok(received.listener4, "onClose handler called");
166 windows.removeListener('open', listener1);
167 windows.removeListener('open', listener2);
168 windows.removeListener('close', listener3);
169 windows.removeListener('close', listener4);
171 done();
172 });
173 }
174 });
175 };
177 exports.testActiveWindow = function(assert, done) {
178 let windows = browserWindows;
180 // API window objects
181 let window2, window3;
183 // Raw window objects
184 let rawWindow2, rawWindow3;
186 let testSteps = [
187 function() {
188 assert.equal(windows.length, 3, "Correct number of browser windows");
190 let count = 0;
191 for (let window in windows)
192 count++;
193 assert.equal(count, 3, "Correct number of windows returned by iterator");
195 assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
197 continueAfterFocus(rawWindow2);
198 rawWindow2.focus();
199 },
200 function() {
201 assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
203 continueAfterFocus(rawWindow2);
204 window2.activate();
205 },
206 function() {
207 assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
209 continueAfterFocus(rawWindow3);
210 window3.activate();
211 },
212 function() {
213 assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
214 finishTest();
215 }
216 ];
218 let newWindow = null;
219 let tracker = new WindowTracker({
220 onTrack: function(window) {
221 newWindow = window;
222 }
223 });
225 windows.open({
226 url: "data:text/html;charset=utf-8,<title>window 2</title>",
227 onOpen: function(window) {
228 assert.pass('window 2 open');
230 window.tabs.activeTab.on('ready', function() {
231 assert.pass('window 2 tab activated');
233 window2 = window;
234 assert.ok(newWindow, "A new window was opened");
235 rawWindow2 = newWindow;
236 newWindow = null;
238 assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
239 assert.equal(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
241 windows.open({
242 url: "data:text/html;charset=utf-8,<title>window 3</title>",
243 onOpen: function(window) {
244 assert.pass('window 3 open');
246 window.tabs.activeTab.on('ready', function onReady() {
247 assert.pass('window 3 tab activated');
249 window3 = window;
250 assert.ok(newWindow, "A new window was opened");
251 rawWindow3 = newWindow;
252 tracker.unload();
254 assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
255 assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
257 continueAfterFocus(rawWindow3);
258 rawWindow3.focus();
259 });
260 }
261 });
262 });
263 }
264 });
266 function nextStep() {
267 if (testSteps.length) {
268 setTimeout(testSteps.shift())
269 }
270 }
272 let continueAfterFocus = function(w) onFocus(w).then(nextStep);
274 function finishTest() {
275 // close unactive window first to avoid unnecessary focus changing
276 window2.close(function() {
277 window3.close(function() {
278 assert.equal(rawWindow2.closed, true, 'window 2 is closed');
279 assert.equal(rawWindow3.closed, true, 'window 3 is closed');
281 done();
282 });
283 });
284 }
285 };
287 exports.testTrackWindows = function(assert, done) {
288 let windows = [];
289 let actions = [];
291 let expects = [
292 "activate 0", "global activate 0", "deactivate 0", "global deactivate 0",
293 "activate 1", "global activate 1", "deactivate 1", "global deactivate 1",
294 "activate 2", "global activate 2"
295 ];
297 function windowsActivation(window) {
298 let index = windows.indexOf(window);
299 // only concerned with windows opened for this test
300 if (index < 0)
301 return;
303 assert.equal(actions.join(), expects.slice(0, index*4 + 1).join(), expects[index*4 + 1]);
304 actions.push("global activate " + index)
305 }
307 function windowsDeactivation(window) {
308 let index = windows.indexOf(window);
309 // only concerned with windows opened for this test
310 if (index < 0)
311 return;
313 assert.equal(actions.join(), expects.slice(0, index*4 + 3).join(), expects[index*4 + 3]);
314 actions.push("global deactivate " + index)
315 }
317 // listen to global activate events
318 browserWindows.on("activate", windowsActivation);
320 // listen to global deactivate events
321 browserWindows.on("deactivate", windowsDeactivation);
324 function openWindow() {
325 windows.push(browserWindows.open({
326 url: "data:text/html;charset=utf-8,<i>testTrackWindows</i>",
327 onActivate: function(window) {
328 let index = windows.indexOf(window);
330 // Guard against windows that have already been removed.
331 // See bug 874502 comment 32.
332 if (index == -1)
333 return;
335 assert.equal(actions.join(),
336 expects.slice(0, index*4).join(),
337 "expecting " + expects[index*4]);
338 actions.push("activate " + index);
340 if (windows.length < 3) {
341 openWindow()
342 }
343 else {
344 (function closeWindows(windows) {
345 if (!windows.length) {
346 browserWindows.removeListener("activate", windowsActivation);
347 browserWindows.removeListener("deactivate", windowsDeactivation);
348 return done();
349 }
351 return windows.pop().close(function() {
352 assert.pass('window was closed');
353 closeWindows(windows);
354 });
355 })(windows)
356 }
357 },
358 onDeactivate: function(window) {
359 let index = windows.indexOf(window);
361 // Guard against windows that have already been removed.
362 // See bug 874502 comment 32.
363 if (index == -1)
364 return;
366 assert.equal(actions.join(),
367 expects.slice(0, index*4 + 2).join(),
368 "expecting " + expects[index*4 + 2]);
369 actions.push("deactivate " + index)
370 }
371 }));
372 }
373 openWindow();
374 }
376 // test that it is not possible to open a private window by default
377 exports.testWindowOpenPrivateDefault = function(assert, done) {
378 browserWindows.open({
379 url: 'about:mozilla',
380 isPrivate: true,
381 onOpen: function(window) {
382 let tab = window.tabs[0];
384 tab.once('ready', function() {
385 assert.equal(tab.url, 'about:mozilla', 'opened correct tab');
386 assert.equal(isPrivate(tab), false, 'tab is not private');
388 window.close(done);
389 });
390 }
391 });
392 }
394 // test that it is not possible to find a private window in
395 // windows module's iterator
396 exports.testWindowIteratorPrivateDefault = function(assert, done) {
397 assert.equal(browserWindows.length, 1, 'only one window open');
399 open('chrome://browser/content/browser.xul', {
400 features: {
401 private: true,
402 chrome: true
403 }
404 }).then(focus).then(function(window) {
405 // test that there is a private window opened
406 assert.equal(isPrivate(window), isWindowPBSupported, 'there is a private window open');
407 assert.strictEqual(window, winUtils.activeWindow);
408 assert.strictEqual(window, getMostRecentWindow());
410 assert.ok(!isPrivate(browserWindows.activeWindow));
412 assert.equal(browserWindows.length, 1, 'only one window in browserWindows');
413 assert.equal(windows().length, 1, 'only one window in windows()');
415 assert.equal(windows(null, { includePrivate: true }).length, 2);
417 // test that all windows in iterator are not private
418 for (let window of browserWindows)
419 assert.ok(!isPrivate(window), 'no window in browserWindows is private');
421 close(window).then(done);
422 });
423 };
425 exports["test getView(window)"] = function(assert, done) {
426 browserWindows.once("open", window => {
427 const view = viewFor(window);
429 assert.ok(view instanceof Ci.nsIDOMWindow, "view is a window");
430 assert.ok(isBrowser(view), "view is a browser window");
431 assert.equal(getWindowTitle(view), window.title,
432 "window has a right title");
434 window.close();
435 // Defer handler cause window is destroyed after event is dispatched.
436 browserWindows.once("close", defer(_ => {
437 assert.equal(viewFor(window), null, "window view is gone");
438 done();
439 }));
440 });
442 browserWindows.open({ url: "data:text/html,<title>yo</title>" });
443 };
445 require('sdk/test').run(exports);