|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 "use strict"; |
|
5 |
|
6 const INITIAL_VALUE = "browser_broadcast.js-initial-value-" + Date.now(); |
|
7 |
|
8 /** |
|
9 * This test ensures we won't lose tab data queued in the content script when |
|
10 * closing a tab. |
|
11 */ |
|
12 add_task(function flush_on_tabclose() { |
|
13 let tab = yield createTabWithStorageData(["http://example.com"]); |
|
14 let browser = tab.linkedBrowser; |
|
15 |
|
16 yield modifySessionStorage(browser, {test: "on-tab-close"}); |
|
17 gBrowser.removeTab(tab); |
|
18 |
|
19 let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window)); |
|
20 is(storage["http://example.com"].test, "on-tab-close", |
|
21 "sessionStorage data has been flushed on TabClose"); |
|
22 }); |
|
23 |
|
24 /** |
|
25 * This test ensures we won't lose tab data queued in the content script when |
|
26 * the application tries to quit. |
|
27 */ |
|
28 add_task(function flush_on_quit_requested() { |
|
29 let tab = yield createTabWithStorageData(["http://example.com"]); |
|
30 let browser = tab.linkedBrowser; |
|
31 |
|
32 yield modifySessionStorage(browser, {test: "on-quit-requested"}); |
|
33 |
|
34 // Note that sending quit-application-requested should not interfere with |
|
35 // other tests and code. We're just notifying about a shutdown request but |
|
36 // we will not send quit-application-granted. Observers will thus assume |
|
37 // that some other observer has canceled the request. |
|
38 sendQuitApplicationRequested(); |
|
39 |
|
40 let {storage} = JSON.parse(ss.getTabState(tab)); |
|
41 is(storage["http://example.com"].test, "on-quit-requested", |
|
42 "sessionStorage data has been flushed when a quit is requested"); |
|
43 |
|
44 gBrowser.removeTab(tab); |
|
45 }); |
|
46 |
|
47 /** |
|
48 * This test ensures we won't lose tab data queued in the content script when |
|
49 * duplicating a tab. |
|
50 */ |
|
51 add_task(function flush_on_duplicate() { |
|
52 let tab = yield createTabWithStorageData(["http://example.com"]); |
|
53 let browser = tab.linkedBrowser; |
|
54 |
|
55 yield modifySessionStorage(browser, {test: "on-duplicate"}); |
|
56 let tab2 = ss.duplicateTab(window, tab); |
|
57 let {storage} = JSON.parse(ss.getTabState(tab2)); |
|
58 is(storage["http://example.com"].test, "on-duplicate", |
|
59 "sessionStorage data has been flushed when duplicating tabs"); |
|
60 |
|
61 yield promiseTabRestored(tab2); |
|
62 gBrowser.removeTab(tab2) |
|
63 let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window)); |
|
64 is(storage["http://example.com"].test, "on-duplicate", |
|
65 "sessionStorage data has been flushed when duplicating tabs"); |
|
66 |
|
67 gBrowser.removeTab(tab); |
|
68 }); |
|
69 |
|
70 /** |
|
71 * This test ensures we won't lose tab data queued in the content script when |
|
72 * a window is closed. |
|
73 */ |
|
74 add_task(function flush_on_windowclose() { |
|
75 let win = yield promiseNewWindow(); |
|
76 let tab = yield createTabWithStorageData(["http://example.com"], win); |
|
77 let browser = tab.linkedBrowser; |
|
78 |
|
79 yield modifySessionStorage(browser, {test: "on-window-close"}); |
|
80 yield closeWindow(win); |
|
81 |
|
82 let [{tabs: [_, {storage}]}] = JSON.parse(ss.getClosedWindowData()); |
|
83 is(storage["http://example.com"].test, "on-window-close", |
|
84 "sessionStorage data has been flushed when closing a window"); |
|
85 }); |
|
86 |
|
87 /** |
|
88 * This test ensures that stale tab data is ignored when reusing a tab |
|
89 * (via e.g. setTabState) and does not overwrite the new data. |
|
90 */ |
|
91 add_task(function flush_on_settabstate() { |
|
92 let tab = yield createTabWithStorageData(["http://example.com"]); |
|
93 let browser = tab.linkedBrowser; |
|
94 |
|
95 // Flush to make sure our tab state is up-to-date. |
|
96 SyncHandlers.get(browser).flush(); |
|
97 |
|
98 let state = ss.getTabState(tab); |
|
99 yield modifySessionStorage(browser, {test: "on-set-tab-state"}); |
|
100 |
|
101 // Flush all data contained in the content script but send it using |
|
102 // asynchronous messages. |
|
103 SyncHandlers.get(browser).flushAsync(); |
|
104 |
|
105 ss.setTabState(tab, state); |
|
106 yield promiseTabRestored(tab); |
|
107 |
|
108 let {storage} = JSON.parse(ss.getTabState(tab)); |
|
109 is(storage["http://example.com"].test, INITIAL_VALUE, |
|
110 "sessionStorage data has not been overwritten"); |
|
111 |
|
112 gBrowser.removeTab(tab); |
|
113 }); |
|
114 |
|
115 /** |
|
116 * This test ensures that we won't lose tab data that has been sent |
|
117 * asynchronously just before closing a tab. Flushing must re-send all data |
|
118 * that hasn't been received by chrome, yet. |
|
119 */ |
|
120 add_task(function flush_on_tabclose_racy() { |
|
121 let tab = yield createTabWithStorageData(["http://example.com"]); |
|
122 let browser = tab.linkedBrowser; |
|
123 |
|
124 // Flush to make sure we start with an empty queue. |
|
125 SyncHandlers.get(browser).flush(); |
|
126 |
|
127 yield modifySessionStorage(browser, {test: "on-tab-close-racy"}); |
|
128 |
|
129 // Flush all data contained in the content script but send it using |
|
130 // asynchronous messages. |
|
131 SyncHandlers.get(browser).flushAsync(); |
|
132 gBrowser.removeTab(tab); |
|
133 |
|
134 let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window)); |
|
135 is(storage["http://example.com"].test, "on-tab-close-racy", |
|
136 "sessionStorage data has been merged correctly to prevent data loss"); |
|
137 }); |
|
138 |
|
139 function promiseNewWindow() { |
|
140 let deferred = Promise.defer(); |
|
141 whenNewWindowLoaded({private: false}, deferred.resolve); |
|
142 return deferred.promise; |
|
143 } |
|
144 |
|
145 function closeWindow(win) { |
|
146 let deferred = Promise.defer(); |
|
147 let outerID = win.QueryInterface(Ci.nsIInterfaceRequestor) |
|
148 .getInterface(Ci.nsIDOMWindowUtils) |
|
149 .outerWindowID; |
|
150 |
|
151 Services.obs.addObserver(function obs(subject, topic) { |
|
152 let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data; |
|
153 if (id == outerID) { |
|
154 Services.obs.removeObserver(obs, topic); |
|
155 deferred.resolve(); |
|
156 } |
|
157 }, "outer-window-destroyed", false); |
|
158 |
|
159 win.close(); |
|
160 return deferred.promise; |
|
161 } |
|
162 |
|
163 function createTabWithStorageData(urls, win = window) { |
|
164 return Task.spawn(function task() { |
|
165 let tab = win.gBrowser.addTab(); |
|
166 let browser = tab.linkedBrowser; |
|
167 |
|
168 for (let url of urls) { |
|
169 browser.loadURI(url); |
|
170 yield promiseBrowserLoaded(browser); |
|
171 yield modifySessionStorage(browser, {test: INITIAL_VALUE}); |
|
172 } |
|
173 |
|
174 throw new Task.Result(tab); |
|
175 }); |
|
176 } |
|
177 |
|
178 function waitForStorageEvent(browser) { |
|
179 return promiseContentMessage(browser, "ss-test:MozStorageChanged"); |
|
180 } |
|
181 |
|
182 function sendQuitApplicationRequested() { |
|
183 let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"] |
|
184 .createInstance(Ci.nsISupportsPRBool); |
|
185 Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); |
|
186 } |
|
187 |
|
188 function modifySessionStorage(browser, data) { |
|
189 browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data); |
|
190 return waitForStorageEvent(browser); |
|
191 } |