summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/general/browser_restore_isAppTab.js
blob: e20974d8022e2f70f76e3b1e80b0f117b43eccf8 (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});

const DUMMY = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";

function getMinidumpDirectory() {
  let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
  dir.append("minidumps");
  return dir;
}

// This observer is needed so we can clean up all evidence of the crash so
// the testrunner thinks things are peachy.
var CrashObserver = {
  observe: function(subject, topic, data) {
    is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
    ok(subject instanceof Ci.nsIPropertyBag2,
       'Subject implements nsIPropertyBag2.');
    // we might see this called as the process terminates due to previous tests.
    // We are only looking for "abnormal" exits...
    if (!subject.hasKey("abnormal")) {
      info("This is a normal termination and isn't the one we are looking for...");
      return;
    }

    let dumpID;
    if ('nsICrashReporter' in Ci) {
      dumpID = subject.getPropertyAsAString('dumpID');
      ok(dumpID, "dumpID is present and not an empty string");
    }

    if (dumpID) {
      let minidumpDirectory = getMinidumpDirectory();
      let file = minidumpDirectory.clone();
      file.append(dumpID + '.dmp');
      file.remove(true);
      file = minidumpDirectory.clone();
      file.append(dumpID + '.extra');
      file.remove(true);
    }
  }
}
Services.obs.addObserver(CrashObserver, 'ipc:content-shutdown', false);

registerCleanupFunction(() => {
  Services.obs.removeObserver(CrashObserver, 'ipc:content-shutdown');
});

function frameScript() {
  addMessageListener("Test:GetIsAppTab", function() {
    sendAsyncMessage("Test:IsAppTab", { isAppTab: docShell.isAppTab });
  });

  addMessageListener("Test:Crash", function() {
    privateNoteIntentionalCrash();
    Components.utils.import("resource://gre/modules/ctypes.jsm");
    let zero = new ctypes.intptr_t(8);
    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
    badptr.contents
  });
}

function loadFrameScript(browser) {
  browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
}

function isBrowserAppTab(browser) {
  return new Promise(resolve => {
    function listener({ data }) {
      browser.messageManager.removeMessageListener("Test:IsAppTab", listener);
      resolve(data.isAppTab);
    }
    // It looks like same-process messages may be reordered by the message
    // manager, so we need to wait one tick before sending the message.
    executeSoon(function () {
      browser.messageManager.addMessageListener("Test:IsAppTab", listener);
      browser.messageManager.sendAsyncMessage("Test:GetIsAppTab");
    });
  });
}

// Restarts the child process by crashing it then reloading the tab
var restart = Task.async(function*(browser) {
  // If the tab isn't remote this would crash the main process so skip it
  if (!browser.isRemoteBrowser)
    return;

  // Make sure the main process has all of the current tab state before crashing
  yield TabStateFlusher.flush(browser);

  browser.messageManager.sendAsyncMessage("Test:Crash");
  yield promiseWaitForEvent(browser, "AboutTabCrashedLoad", false, true);

  let tab = gBrowser.getTabForBrowser(browser);
  SessionStore.reviveCrashedTab(tab);

  yield promiseTabLoaded(tab);
});

add_task(function* navigate() {
  let tab = gBrowser.addTab("about:robots");
  let browser = tab.linkedBrowser;
  gBrowser.selectedTab = tab;
  yield waitForDocLoadComplete();
  loadFrameScript(browser);
  let isAppTab = yield isBrowserAppTab(browser);
  ok(!isAppTab, "Docshell shouldn't think it is an app tab");

  gBrowser.pinTab(tab);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  gBrowser.loadURI(DUMMY);
  yield waitForDocLoadComplete();
  loadFrameScript(browser);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  gBrowser.unpinTab(tab);
  isAppTab = yield isBrowserAppTab(browser);
  ok(!isAppTab, "Docshell shouldn't think it is an app tab");

  gBrowser.pinTab(tab);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  gBrowser.loadURI("about:robots");
  yield waitForDocLoadComplete();
  loadFrameScript(browser);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  gBrowser.removeCurrentTab();
});

add_task(function* crash() {
  if (!gMultiProcessBrowser || !("nsICrashReporter" in Ci))
    return;

  let tab = gBrowser.addTab(DUMMY);
  let browser = tab.linkedBrowser;
  gBrowser.selectedTab = tab;
  yield waitForDocLoadComplete();
  loadFrameScript(browser);
  let isAppTab = yield isBrowserAppTab(browser);
  ok(!isAppTab, "Docshell shouldn't think it is an app tab");

  gBrowser.pinTab(tab);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  yield restart(browser);
  loadFrameScript(browser);
  isAppTab = yield isBrowserAppTab(browser);
  ok(isAppTab, "Docshell should think it is an app tab");

  gBrowser.removeCurrentTab();
});