summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/windows/test-firefox-windows.js
blob: 4db9953eb8c365bb3bc99adf8f8824adec72e788 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';

const { Cc, Ci } = require('chrome');
const { setTimeout } = require('sdk/timers');
const { Loader } = require('sdk/test/loader');
const { onFocus, getMostRecentWindow, windows, isBrowser, getWindowTitle, isFocused } = require('sdk/window/utils');
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
const { browserWindows } = require("sdk/windows");
const tabs = require("sdk/tabs");
const winUtils = require("sdk/deprecated/window-utils");
const { isPrivate } = require('sdk/private-browsing');
const { isWindowPBSupported } = require('sdk/private-browsing/utils');
const { viewFor } = require("sdk/view/core");
const { defer } = require("sdk/lang/functional");
const { cleanUI } = require("sdk/test/utils");
const { after } = require("sdk/test/utils");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
const { openTab } = require("../tabs/utils");
const { set: setPref, reset: resetPref } = require("sdk/preferences/service");
const OPEN_IN_NEW_WINDOW_PREF = 'browser.link.open_newwindow';
const DISABLE_POPUP_PREF = 'dom.disable_open_during_load';
const { wait } = require('../event/helpers');

// TEST: open & close window
exports.testOpenAndCloseWindow = function(assert, done) {
  assert.equal(browserWindows.length, 1, "Only one window open");
  let title = 'testOpenAndCloseWindow';

  browserWindows.open({
    url: "data:text/html;charset=utf-8,<title>" + title + "</title>",
    onOpen: function(window) {
      assert.equal(this, browserWindows, "The 'this' object is the windows object.");
      assert.equal(window.tabs.length, 1, "Only one tab open");
      assert.equal(browserWindows.length, 2, "Two windows open");

      window.tabs.activeTab.once('ready', function onReady(tab) {
        assert.pass(RegExp(title).test(window.title), "URL correctly loaded");
        window.close();
      });
    },
    onClose: function(window) {
      assert.equal(window.tabs.length, 0, "Tabs were cleared");
      assert.equal(browserWindows.length, 1, "Only one window open");
      done();
    }
  });
};

exports.testNeWindowIsFocused = function(assert, done) {
  let mainWindow = browserWindows.activeWindow;

  browserWindows.open({
    url: "about:blank",
    onOpen: function(window) {
      focus(viewFor(window)).then((window) => {
        assert.ok(isFocused(window), 'the new window is focused');
        assert.ok(isFocused(browserWindows.activeWindow), 'the active window is focused');
        assert.ok(!isFocused(mainWindow), 'the main window is not focused');
        done();
      })
    }
  });
}

exports.testOpenRelativePathWindow = function*(assert) {
  assert.equal(browserWindows.length, 1, "Only one window open");

  let loader = Loader(module, null, null, {
    modules: {
      "sdk/self": merge({}, self, {
        data: merge({}, self.data, require("./../fixtures"))
      })
    }
  });
  assert.pass("Created a new loader");

  let tabReady = new Promise(resolve => {
    loader.require("sdk/tabs").on("ready", (tab) => {
      if (!/test\.html$/.test(tab.url))
        return;
      assert.equal(tab.title, "foo",
        "tab opened a document with relative path");
      resolve();
    });
  });


  yield new Promise(resolve => {
    loader.require("sdk/windows").browserWindows.open({
      url: "./test.html",
      onOpen: (window) => {
        assert.pass("Created a new window");
        resolve();
      }
    })
  });

  yield tabReady;
  loader.unload();
}

exports.testAutomaticDestroy = function(assert, done) {
  let windows = browserWindows;

  // Create a second windows instance that we will unload
  let called = false;
  let loader = Loader(module);
  let windows2 = loader.require("sdk/windows").browserWindows;

  windows2.on("open", function() {
    called = true;
  });

  loader.unload();

  // Fire a windows event and check that this unloaded instance is inactive
  windows.open({
    url: "data:text/html;charset=utf-8,foo",
    onOpen: function(window) {
      setTimeout(function () {
        assert.ok(!called, "Unloaded windows instance is destroyed and inactive");
        done();
      });
    }
  });
};

exports.testWindowTabsObject = function(assert, done) {
  let window, count = 0;
  function runTest() {
    if (++count != 2)
      return;

    assert.equal(window.tabs.length, 1, "Only 1 tab open");
    assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");

    window.tabs.open({
      url: "data:text/html;charset=utf-8,<title>tab 2</title>",
      inBackground: true,
      onReady: function onReady(newTab) {
        assert.equal(window.tabs.length, 2, "New tab open");
        assert.equal(newTab.title, "tab 2", "Correct new tab title");
        assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");

        let i = 1;
        for (let tab of window.tabs)
          assert.equal(tab.title, "tab " + i++, "Correct title");

        window.close();
      }
    });
  }

  tabs.once("ready", runTest);

  browserWindows.open({
    url: "data:text/html;charset=utf-8,<title>tab 1</title>",
    onActivate: function onActivate(win) {
      window = win;
      runTest();
    },
    onClose: function onClose(window) {
      assert.equal(window.tabs.length, 0, "No more tabs on closed window");
      done();
    }
  });
};

exports.testOnOpenOnCloseListeners = function(assert, done) {
  let windows = browserWindows;

  assert.equal(browserWindows.length, 1, "Only one window open");

  let received = {
    listener1: false,
    listener2: false,
    listener3: false,
    listener4: false
  }

  function listener1() {
    assert.equal(this, windows, "The 'this' object is the windows object.");

    if (received.listener1)
      assert.fail("Event received twice");
    received.listener1 = true;
  }

  function listener2() {
    if (received.listener2)
      assert.fail("Event received twice");
    received.listener2 = true;
  }

  function listener3() {
    assert.equal(this, windows, "The 'this' object is the windows object.");
    if (received.listener3)
      assert.fail("Event received twice");
    received.listener3 = true;
  }

  function listener4() {
    if (received.listener4)
      assert.fail("Event received twice");
    received.listener4 = true;
  }

  windows.on('open', listener1);
  windows.on('open', listener2);
  windows.on('close', listener3);
  windows.on('close', listener4);

  windows.open({
    url: "data:text/html;charset=utf-8,foo",
    onOpen: function(window) {
      window.close(function() {
        assert.ok(received.listener1, "onOpen handler called");
        assert.ok(received.listener2, "onOpen handler called");
        assert.ok(received.listener3, "onClose handler called");
        assert.ok(received.listener4, "onClose handler called");

        windows.removeListener('open', listener1);
        windows.removeListener('open', listener2);
        windows.removeListener('close', listener3);
        windows.removeListener('close', listener4);

        done();
      });
    }
  });
};

exports.testActiveWindow = function*(assert) {
  let windows = browserWindows;
  let window = getMostRecentWindow();

  // Raw window objects
  let rawWindow2 =  yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);
  assert.pass("Window 2 was created");

  // open a tab in window 2
  yield openTab(rawWindow2, "data:text/html;charset=utf-8,<title>window 2</title>");

  assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
  assert.equal(rawWindow2.document.title, windows[1].title, "Saw correct title on window 2");

  let rawWindow3 =  yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);;
  assert.pass("Window 3 was created");

  // open a tab in window 3
  yield openTab(rawWindow3, "data:text/html;charset=utf-8,<title>window 3</title>");

  assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
  assert.equal(rawWindow3.document.title, windows[2].title, "Saw correct title on window 3");

  assert.equal(windows.length, 3, "Correct number of browser windows");

  let count = 0;
  for (let window in windows) {
    count++;
  }
  assert.equal(count, 3, "Correct number of windows returned by iterator");
  assert.equal(windows.activeWindow.title, windows[2].title, "Correct active window title - 3");
  let window3 = windows[2];

  yield focus(rawWindow2);

  assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window title - 2");
  let window2 = windows[1];

  yield new Promise(resolve => {
    onFocus(rawWindow2).then(resolve);
    window2.activate();
    assert.pass("activating window2");
  });

  assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window - 2");

  yield new Promise(resolve => {
    onFocus(rawWindow3).then(resolve);
    window3.activate();
    assert.pass("activating window3");
  });

  assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");

  yield close(rawWindow2);
  yield close(rawWindow3);

  assert.equal(windows.length, 1, "Correct number of browser windows");
  assert.equal(window.closed, false, "Original window is still open");
};

exports.testTrackWindows = function(assert, done) {
  let windows = [];
  let actions = [];

  let expects = [
    "activate 0", "global activate 0", "deactivate 0", "global deactivate 0",
    "activate 1", "global activate 1", "deactivate 1", "global deactivate 1",
    "activate 2", "global activate 2"
  ];

  function windowsActivation(window) {
    let index = windows.indexOf(window);
    // only concerned with windows opened for this test
    if (index < 0)
      return;

    assert.equal(actions.join(), expects.slice(0, index*4 + 1).join(), expects[index*4 + 1]);
    actions.push("global activate " + index)
  }

  function windowsDeactivation(window) {
    let index = windows.indexOf(window);
    // only concerned with windows opened for this test
    if (index < 0)
      return;

    assert.equal(actions.join(), expects.slice(0, index*4 + 3).join(), expects[index*4 + 3]);
    actions.push("global deactivate " + index)
  }

  // listen to global activate events
  browserWindows.on("activate", windowsActivation);

  // listen to global deactivate events
  browserWindows.on("deactivate", windowsDeactivation);

  function openWindow() {
    windows.push(browserWindows.open({
      url: "data:text/html;charset=utf-8,<i>testTrackWindows</i>",
      onActivate: function(window) {
        let index = windows.indexOf(window);

        // Guard against windows that have already been removed.
        // See bug 874502 comment 32.
        if (index == -1)
          return;

        assert.equal(actions.join(),
                     expects.slice(0, index*4).join(),
                     "expecting " + expects[index*4]);
        actions.push("activate " + index);

        if (windows.length < 3) {
          openWindow()
        }
        else {
          (function closeWindows(windows) {
            if (!windows.length) {
              assert.pass('the last window was closed');
              browserWindows.removeListener("activate", windowsActivation);
              browserWindows.removeListener("deactivate", windowsDeactivation);
              assert.pass('removed listeners');
              return done();
            }

            return windows.pop().close(function() {
              assert.pass('window was closed');
              closeWindows(windows);
            });
          })(windows)
        }
      },
      onDeactivate: function(window) {
        let index = windows.indexOf(window);

        // Guard against windows that have already been removed.
        // See bug 874502 comment 32.
        if (index == -1)
          return;

        assert.equal(actions.join(),
                     expects.slice(0, index*4 + 2).join(),
                     "expecting " + expects[index*4 + 2]);
        actions.push("deactivate " + index)
      }
    }));
  }
  openWindow();
}

// test that it is not possible to open a private window by default
exports.testWindowOpenPrivateDefault = function*(assert) {
  const TITLE = "yo";
  const URL = "data:text/html,<title>" + TITLE + "</title>";

  let tabReady = new Promise(resolve => {
    tabs.on('ready', function onTabReady(tab) {
      if (tab.url != URL)
        return;

      tabs.removeListener('ready', onTabReady);
      assert.equal(tab.title, TITLE, 'opened correct tab');
      assert.equal(isPrivate(tab), false, 'tab is not private');
      resolve();
    });
  })

  yield new Promise(resolve => browserWindows.open({
    url: URL,
    isPrivate: true,
    onOpen: function(window) {
      assert.pass("the new window was opened");
      resolve();
    }
  }));

  yield tabReady;
}

// test that it is not possible to find a private window in
// windows module's iterator
exports.testWindowIteratorPrivateDefault = function*(assert) {
  assert.equal(browserWindows.length, 1, 'only one window open');

  let window = yield open('chrome://browser/content/browser.xul', {
    features: {
      private: true,
      chrome: true
    }
  });

  yield focus(window);

  // test that there is a private window opened
  assert.equal(isPrivate(window), true, 'there is a private window open');
  assert.strictEqual(window, winUtils.activeWindow);
  assert.strictEqual(window, getMostRecentWindow());

  assert.ok(!isPrivate(browserWindows.activeWindow));

  assert.equal(browserWindows.length, 1, 'only one window in browserWindows');
  assert.equal(windows().length, 1, 'only one window in windows()');

  assert.equal(windows(null, { includePrivate: true }).length, 2);

  // test that all windows in iterator are not private
  for (let window of browserWindows) {
    assert.ok(!isPrivate(window), 'no window in browserWindows is private');
  }
};

exports["test getView(window)"] = function(assert, done) {
  browserWindows.once("open", window => {
    const view = viewFor(window);

    assert.ok(view instanceof Ci.nsIDOMWindow, "view is a window");
    assert.ok(isBrowser(view), "view is a browser window");
    assert.equal(getWindowTitle(view), window.title,
                 "window has a right title");

    window.close();
    // Defer handler cause window is destroyed after event is dispatched.
    browserWindows.once("close", defer(_ => {
      assert.equal(viewFor(window), null, "window view is gone");
      done();
    }));
  });

  browserWindows.open({ url: "data:text/html,<title>yo</title>" });
};

// Tests that the this property is correct in event listeners called from
// the windows API.
exports.testWindowEventBindings = function(assert, done) {
  let loader = Loader(module);
  let { browserWindows } = loader.require("sdk/windows");
  let firstWindow = browserWindows.activeWindow;
  let { viewFor } = loader.require("sdk/view/core");

  let EVENTS = ["open", "close", "activate", "deactivate"];

  let windowBoundEventHandler = (event) => function(window) {
    // Close event listeners are always bound to browserWindows.
    if (event == "close")
      assert.equal(this, browserWindows, "window listener for " + event + " event should be bound to the browserWindows object.");
    else
      assert.equal(this, window, "window listener for " + event + " event should be bound to the window object.");
  };

  let windowsBoundEventHandler = (event) => function(window) {
    assert.equal(this, browserWindows, "windows listener for " + event + " event should be bound to the browserWindows object.");
  }
  for (let event of EVENTS)
    browserWindows.on(event, windowsBoundEventHandler(event));

  let windowsOpenEventHandler = (event) => function(window) {
    // Open and close event listeners are always bound to browserWindows.
    if (event == "open" || event == "close")
      assert.equal(this, browserWindows, "windows open listener for " + event + " event should be bound to the browserWindows object.");
    else
      assert.equal(this, window, "windows open listener for " + event + " event should be bound to the window object.");
  }

  let openArgs = {
    url: "data:text/html;charset=utf-8,binding-test",
    onOpen: function(window) {
      windowsOpenEventHandler("open").call(this, window);

      for (let event of EVENTS)
        window.on(event, windowBoundEventHandler(event));

      let rawWindow = viewFor(window);
      onFocus(rawWindow).then(() => {
        window.once("deactivate", () => {
          window.once("close", () => {
            loader.unload();
            done();
          });

          window.close();
        });

        firstWindow.activate();
      });
    }
  };
  // Listen to everything except onOpen
  for (let event of EVENTS.slice(1)) {
    openArgs["on" + event.slice(0, 1).toUpperCase() + event.slice(1)] = windowsOpenEventHandler(event);
  }

  browserWindows.open(openArgs);
}

// Tests that the this property is correct in event listeners called from
// manipulating window.tabs.
exports.testWindowTabEventBindings = function(assert, done) {
  let loader = Loader(module);
  let { browserWindows } = loader.require("sdk/windows");
  let firstWindow = browserWindows.activeWindow;
  let tabs = loader.require("sdk/tabs");
  let windowTabs = firstWindow.tabs;
  let firstTab = firstWindow.tabs.activeTab;

  let EVENTS = ["open", "close", "activate", "deactivate",
                "load", "ready", "pageshow"];

  let tabBoundEventHandler = (event) => function(tab) {
    assert.equal(this, tab, "tab listener for " + event + " event should be bound to the tab object.");
  };

  let windowTabsBoundEventHandler = (event) => function(tab) {
    assert.equal(this, windowTabs, "window tabs listener for " + event + " event should be bound to the windowTabs object.");
  }
  for (let event of EVENTS)
    windowTabs.on(event, windowTabsBoundEventHandler(event));

  let windowTabsOpenEventHandler = (event) => function(tab) {
    assert.equal(this, tab, "window tabs open listener for " + event + " event should be bound to the tab object.");
  }

  let openArgs = {
    url: "data:text/html;charset=utf-8,binding-test",
    onOpen: function(tab) {
      windowTabsOpenEventHandler("open").call(this, tab);

      for (let event of EVENTS)
        tab.on(event, tabBoundEventHandler(event));

      tab.once("pageshow", () => {
        tab.once("deactivate", () => {
          tab.once("close", () => {
            loader.unload();
            done();
          });

          tab.close();
        });

        firstTab.activate();
      });
    }
  };
  // Listen to everything except onOpen
  for (let event of EVENTS.slice(1)) {
    let eventProperty = "on" + event.slice(0, 1).toUpperCase() + event.slice(1);
    openArgs[eventProperty] = windowTabsOpenEventHandler(event);
  }

  windowTabs.open(openArgs);
}

// related to bug #989288
// https://bugzilla.mozilla.org/show_bug.cgi?id=989288
exports["test window events after window.open"] = function*(assert, done) {
  // ensure popups open in a new windows and disable popup blocker
  setPref(OPEN_IN_NEW_WINDOW_PREF, 2);
  setPref(DISABLE_POPUP_PREF, false);

  // open window to trigger observers
  tabs.activeTab.attach({
    contentScript: "window.open('about:blank', '', " +
                   "'width=800,height=600,resizable=no,status=no,location=no');"
  });

  let window = yield wait(browserWindows, "open");
  assert.pass("tab open has occured");
  window.close();

  yield wait(browserWindows,"close");
  assert.pass("tab close has occured");
};

after(exports, function*(name, assert) {
  resetPopupPrefs();
  yield cleanUI();
});

const resetPopupPrefs = () => {
  resetPref(OPEN_IN_NEW_WINDOW_PREF);
  resetPref(DISABLE_POPUP_PREF);
};

require('sdk/test').run(exports);