|
1 /* -*- Mode: Javasript; indent-tab-mode: nil; js-indent-level: 2 -*- */ |
|
2 /* |
|
3 * This testcase performs 3 requests against the offline cache. They |
|
4 * are |
|
5 * |
|
6 * - start_cache_nonpinned_app1() |
|
7 * |
|
8 * - Request nsOfflineCacheService to skip pages (4) of app1 on |
|
9 * the cache storage. |
|
10 * |
|
11 * - The offline cache storage is empty at this monent. |
|
12 * |
|
13 * - start_cache_nonpinned_app2_for_partial() |
|
14 * |
|
15 * - Request nsOfflineCacheService to skip pages of app2 on the |
|
16 * cache storage. |
|
17 * |
|
18 * - The offline cache storage has only enough space for one more |
|
19 * additional page. Only first of pages is really in the cache. |
|
20 * |
|
21 * - start_cache_pinned_app2_for_success() |
|
22 * |
|
23 * - Request nsOfflineCacheService to skip pages of app2 on the |
|
24 * cache storage. |
|
25 * |
|
26 * - The offline cache storage has only enough space for one |
|
27 * additional page. But, this is a pinned request, |
|
28 * nsOfflineCacheService will make more space for this request |
|
29 * by discarding app1 (non-pinned) |
|
30 * |
|
31 */ |
|
32 |
|
33 Cu.import("resource://testing-common/httpd.js"); |
|
34 |
|
35 // const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
36 |
|
37 Cu.import("resource://gre/modules/Services.jsm"); |
|
38 |
|
39 const kNS_OFFLINECACHEUPDATESERVICE_CONTRACTID = |
|
40 "@mozilla.org/offlinecacheupdate-service;1"; |
|
41 |
|
42 const kManifest1 = "CACHE MANIFEST\n" + |
|
43 "/pages/foo1\n" + |
|
44 "/pages/foo2\n" + |
|
45 "/pages/foo3\n" + |
|
46 "/pages/foo4\n"; |
|
47 const kManifest2 = "CACHE MANIFEST\n" + |
|
48 "/pages/foo5\n" + |
|
49 "/pages/foo6\n" + |
|
50 "/pages/foo7\n" + |
|
51 "/pages/foo8\n"; |
|
52 |
|
53 const kDataFileSize = 1024; // file size for each content page |
|
54 const kCacheSize = kDataFileSize * 5; // total space for offline cache storage |
|
55 |
|
56 XPCOMUtils.defineLazyGetter(this, "kHttpLocation", function() { |
|
57 return "http://localhost:" + httpServer.identity.primaryPort + "/"; |
|
58 }); |
|
59 |
|
60 XPCOMUtils.defineLazyGetter(this, "kHttpLocation_ip", function() { |
|
61 return "http://127.0.0.1:" + httpServer.identity.primaryPort + "/"; |
|
62 }); |
|
63 |
|
64 function manifest1_handler(metadata, response) { |
|
65 do_print("manifest1\n"); |
|
66 response.setHeader("content-type", "text/cache-manifest"); |
|
67 |
|
68 response.write(kManifest1); |
|
69 } |
|
70 |
|
71 function manifest2_handler(metadata, response) { |
|
72 do_print("manifest2\n"); |
|
73 response.setHeader("content-type", "text/cache-manifest"); |
|
74 |
|
75 response.write(kManifest2); |
|
76 } |
|
77 |
|
78 function app_handler(metadata, response) { |
|
79 do_print("app_handler\n"); |
|
80 response.setHeader("content-type", "text/html"); |
|
81 |
|
82 response.write("<html></html>"); |
|
83 } |
|
84 |
|
85 function datafile_handler(metadata, response) { |
|
86 do_print("datafile_handler\n"); |
|
87 let data = ""; |
|
88 |
|
89 while(data.length < kDataFileSize) { |
|
90 data = data + Math.random().toString(36).substring(2, 15); |
|
91 } |
|
92 |
|
93 response.setHeader("content-type", "text/plain"); |
|
94 response.write(data.substring(0, kDataFileSize)); |
|
95 } |
|
96 |
|
97 var httpServer; |
|
98 |
|
99 function init_profile() { |
|
100 var ps = Cc["@mozilla.org/preferences-service;1"] |
|
101 .getService(Ci.nsIPrefBranch); |
|
102 dump(ps.getBoolPref("browser.cache.offline.enable")); |
|
103 ps.setBoolPref("browser.cache.offline.enable", true); |
|
104 ps.setComplexValue("browser.cache.offline.parent_directory", |
|
105 Ci.nsILocalFile, do_get_profile()); |
|
106 } |
|
107 |
|
108 function init_http_server() { |
|
109 httpServer = new HttpServer(); |
|
110 httpServer.registerPathHandler("/app1", app_handler); |
|
111 httpServer.registerPathHandler("/app2", app_handler); |
|
112 httpServer.registerPathHandler("/app1.appcache", manifest1_handler); |
|
113 httpServer.registerPathHandler("/app2.appcache", manifest2_handler); |
|
114 for (i = 1; i <= 8; i++) { |
|
115 httpServer.registerPathHandler("/pages/foo" + i, datafile_handler); |
|
116 } |
|
117 httpServer.start(-1); |
|
118 } |
|
119 |
|
120 function init_cache_capacity() { |
|
121 let prefs = Cc["@mozilla.org/preferences-service;1"] |
|
122 .getService(Components.interfaces.nsIPrefBranch); |
|
123 prefs.setIntPref("browser.cache.offline.capacity", kCacheSize / 1024); |
|
124 } |
|
125 |
|
126 function clean_app_cache() { |
|
127 evict_cache_entries("appcache"); |
|
128 } |
|
129 |
|
130 function do_app_cache(manifestURL, pageURL, pinned) { |
|
131 let update_service = Cc[kNS_OFFLINECACHEUPDATESERVICE_CONTRACTID]. |
|
132 getService(Ci.nsIOfflineCacheUpdateService); |
|
133 |
|
134 Services.perms.add(manifestURL, |
|
135 "pin-app", |
|
136 pinned ? |
|
137 Ci.nsIPermissionManager.ALLOW_ACTION : |
|
138 Ci.nsIPermissionManager.DENY_ACTION); |
|
139 |
|
140 let update = |
|
141 update_service.scheduleUpdate(manifestURL, |
|
142 pageURL, |
|
143 null); /* no window */ |
|
144 |
|
145 return update; |
|
146 } |
|
147 |
|
148 function watch_update(update, stateChangeHandler, cacheAvailHandler) { |
|
149 let observer = { |
|
150 QueryInterface: function QueryInterface(iftype) { |
|
151 return this; |
|
152 }, |
|
153 |
|
154 updateStateChanged: stateChangeHandler, |
|
155 applicationCacheAvailable: cacheAvailHandler |
|
156 }; |
|
157 update.addObserver(observer, false); |
|
158 |
|
159 return update; |
|
160 } |
|
161 |
|
162 function start_and_watch_app_cache(manifestURL, |
|
163 pageURL, |
|
164 pinned, |
|
165 stateChangeHandler, |
|
166 cacheAvailHandler) { |
|
167 let ioService = Cc["@mozilla.org/network/io-service;1"]. |
|
168 getService(Ci.nsIIOService); |
|
169 let update = do_app_cache(ioService.newURI(manifestURL, null, null), |
|
170 ioService.newURI(pageURL, null, null), |
|
171 pinned); |
|
172 watch_update(update, stateChangeHandler, cacheAvailHandler); |
|
173 return update; |
|
174 } |
|
175 |
|
176 const {STATE_FINISHED: STATE_FINISHED, |
|
177 STATE_CHECKING: STATE_CHECKING, |
|
178 STATE_ERROR: STATE_ERROR } = Ci.nsIOfflineCacheUpdateObserver; |
|
179 |
|
180 /* |
|
181 * Start caching app1 as a non-pinned app. |
|
182 */ |
|
183 function start_cache_nonpinned_app() { |
|
184 do_print("Start non-pinned App1"); |
|
185 start_and_watch_app_cache(kHttpLocation + "app1.appcache", |
|
186 kHttpLocation + "app1", |
|
187 false, |
|
188 function (update, state) { |
|
189 switch(state) { |
|
190 case STATE_FINISHED: |
|
191 start_cache_nonpinned_app2_for_partial(); |
|
192 break; |
|
193 |
|
194 case STATE_ERROR: |
|
195 do_throw("App1 cache state = " + state); |
|
196 break; |
|
197 } |
|
198 }, |
|
199 function (appcahe) { |
|
200 do_print("app1 avail " + appcache + "\n"); |
|
201 }); |
|
202 } |
|
203 |
|
204 /* |
|
205 * Start caching app2 as a non-pinned app. |
|
206 * |
|
207 * This cache request is supposed to be saved partially in the cache |
|
208 * storage for running out of the cache storage. The offline cache |
|
209 * storage can hold 5 files at most. (kDataFileSize bytes for each |
|
210 * file) |
|
211 */ |
|
212 function start_cache_nonpinned_app2_for_partial() { |
|
213 let error_count = [0]; |
|
214 do_print("Start non-pinned App2 for partial\n"); |
|
215 start_and_watch_app_cache(kHttpLocation_ip + "app2.appcache", |
|
216 kHttpLocation_ip + "app2", |
|
217 false, |
|
218 function (update, state) { |
|
219 switch(state) { |
|
220 case STATE_FINISHED: |
|
221 start_cache_pinned_app2_for_success(); |
|
222 break; |
|
223 |
|
224 case STATE_ERROR: |
|
225 do_throw("App2 cache state = " + state); |
|
226 break; |
|
227 } |
|
228 }, |
|
229 function (appcahe) { |
|
230 }); |
|
231 } |
|
232 |
|
233 /* |
|
234 * Start caching app2 as a pinned app. |
|
235 * |
|
236 * This request use IP address (127.0.0.1) as the host name instead of |
|
237 * the one used by app1. Because, app1 is also pinned when app2 is |
|
238 * pinned if they have the same host name (localhost). |
|
239 */ |
|
240 function start_cache_pinned_app2_for_success() { |
|
241 let error_count = [0]; |
|
242 do_print("Start pinned App2 for success\n"); |
|
243 start_and_watch_app_cache(kHttpLocation_ip + "app2.appcache", |
|
244 kHttpLocation_ip + "app2", |
|
245 true, |
|
246 function (update, state) { |
|
247 switch(state) { |
|
248 case STATE_FINISHED: |
|
249 do_check_true(error_count[0] == 0, |
|
250 "Do not discard app1?"); |
|
251 httpServer.stop(do_test_finished); |
|
252 break; |
|
253 |
|
254 case STATE_ERROR: |
|
255 do_print("STATE_ERROR\n"); |
|
256 error_count[0]++; |
|
257 break; |
|
258 } |
|
259 }, |
|
260 function (appcahe) { |
|
261 do_print("app2 avail " + appcache + "\n"); |
|
262 }); |
|
263 } |
|
264 |
|
265 function run_test() { |
|
266 if (typeof _XPCSHELL_PROCESS == "undefined" || |
|
267 _XPCSHELL_PROCESS != "child") { |
|
268 init_profile(); |
|
269 init_cache_capacity(); |
|
270 clean_app_cache(); |
|
271 } |
|
272 |
|
273 init_http_server(); |
|
274 start_cache_nonpinned_app(); |
|
275 do_test_pending(); |
|
276 } |