1 /* import-globals-from head_cache.js */
2 /* import-globals-from head_channels.js */
8 // Expect an existing entry
12 // Return early from onCacheEntryCheck and set the callback to state it expects onCacheEntryCheck to happen
13 const NOTVALID
= 1 << 1;
14 // Throw from onCacheEntryAvailable
15 const THROWAVAIL
= 1 << 2;
16 // Open entry for reading-only
17 const READONLY
= 1 << 3;
18 // Expect the entry to not be found
19 const NOTFOUND
= 1 << 4;
20 // Return ENTRY_NEEDS_REVALIDATION from onCacheEntryCheck
22 // Return ENTRY_PARTIAL from onCacheEntryCheck, in combo with NEW or RECREATE bypasses check for emptiness of the entry
23 const PARTIAL
= 1 << 6;
24 // Expect the entry is doomed, i.e. the output stream should not be possible to open
25 const DOOMED
= 1 << 7;
26 // Don't trigger the go-on callback until the entry is written
27 const WAITFORWRITE
= 1 << 8;
28 // Don't write data (i.e. don't open output stream)
29 const METAONLY
= 1 << 9;
30 // Do recreation of an existing cache entry
31 const RECREATE
= 1 << 10;
32 // Do not give me the entry
33 const NOTWANTED
= 1 << 11;
34 // Tell the cache to wait for the entry to be completely written first
35 const COMPLETE
= 1 << 12;
36 // Don't write meta/data and don't set valid in the callback, consumer will do it manually
37 const DONTFILL
= 1 << 13;
38 // Used in combination with METAONLY, don't call setValid() on the entry after metadata has been set
39 const DONTSETVALID
= 1 << 14;
40 // Notify before checking the data, useful for proper callback ordering checks
41 const NOTIFYBEFOREREAD
= 1 << 15;
42 // It's allowed to not get an existing entry (result of opening is undetermined)
43 const MAYBE_NEW
= 1 << 16;
46 function LOG_C2(o
, m
) {
51 dump("TEST-INFO | CACHE2: " + o
+ "\n");
54 "TEST-INFO | CACHE2: callback #" +
57 (o
.workingData
? o
.workingData
.substr(0, 10) : "---") +
65 function pumpReadStream(inputStream
, goon
) {
66 if (inputStream
.isNonBlocking()) {
67 // non-blocking stream, must read via pump
68 var pump
= Cc
["@mozilla.org/network/input-stream-pump;1"].createInstance(
71 pump
.init(inputStream
, 0, 0, true);
75 onDataAvailable(aRequest
, aInputStream
) {
76 var wrapper
= Cc
["@mozilla.org/scriptableinputstream;1"].createInstance(
77 Ci
.nsIScriptableInputStream
79 wrapper
.init(aInputStream
);
80 var str
= wrapper
.read(wrapper
.available());
81 LOG_C2("reading data '" + str
.substring(0, 5) + "'");
84 onStopRequest(aRequest
, aStatusCode
) {
85 LOG_C2("done reading data: " + aStatusCode
);
86 Assert
.equal(aStatusCode
, Cr
.NS_OK
);
92 let data
= read_stream(inputStream
, inputStream
.available());
97 OpenCallback
.prototype = {
98 QueryInterface
: ChromeUtils
.generateQI(["nsICacheEntryOpenCallback"]),
99 onCacheEntryCheck(entry
) {
100 LOG_C2(this, "onCacheEntryCheck");
101 Assert
.ok(!this.onCheckPassed
);
102 this.onCheckPassed
= true;
104 if (this.behavior
& NOTVALID
) {
105 LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_WANTED");
106 return Ci
.nsICacheEntryOpenCallback
.ENTRY_WANTED
;
109 if (this.behavior
& NOTWANTED
) {
110 LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_NOT_WANTED");
111 return Ci
.nsICacheEntryOpenCallback
.ENTRY_NOT_WANTED
;
114 Assert
.equal(entry
.getMetaDataElement("meto"), this.workingMetadata
);
116 // check for sane flag combination
117 Assert
.notEqual(this.behavior
& (REVAL
| PARTIAL
), REVAL
| PARTIAL
);
119 if (this.behavior
& (REVAL
| PARTIAL
)) {
120 LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_NEEDS_REVALIDATION");
121 return Ci
.nsICacheEntryOpenCallback
.ENTRY_NEEDS_REVALIDATION
;
124 if (this.behavior
& COMPLETE
) {
127 "onCacheEntryCheck DONE, return RECHECK_AFTER_WRITE_FINISHED"
129 // Specific to the new backend because of concurrent read/write:
130 // when a consumer returns RECHECK_AFTER_WRITE_FINISHED from onCacheEntryCheck
131 // the cache calls this callback again after the entry write has finished.
132 // This gives the consumer a chance to recheck completeness of the entry
134 // Thus, we reset state as onCheck would have never been called.
135 this.onCheckPassed
= false;
136 // Don't return RECHECK_AFTER_WRITE_FINISHED on second call of onCacheEntryCheck.
137 this.behavior
&= ~COMPLETE
;
138 return Ci
.nsICacheEntryOpenCallback
.RECHECK_AFTER_WRITE_FINISHED
;
141 LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_WANTED");
142 return Ci
.nsICacheEntryOpenCallback
.ENTRY_WANTED
;
144 onCacheEntryAvailable(entry
, isnew
, status
) {
145 if (this.behavior
& MAYBE_NEW
&& isnew
) {
146 this.behavior
|= NEW
;
149 LOG_C2(this, "onCacheEntryAvailable, " + this.behavior
);
150 Assert
.ok(!this.onAvailPassed
);
151 this.onAvailPassed
= true;
153 Assert
.equal(isnew
, !!(this.behavior
& NEW
));
155 if (this.behavior
& (NOTFOUND
| NOTWANTED
)) {
156 Assert
.equal(status
, Cr
.NS_ERROR_CACHE_KEY_NOT_FOUND
);
158 if (this.behavior
& THROWAVAIL
) {
159 this.throwAndNotify(entry
);
162 } else if (this.behavior
& (NEW
| RECREATE
)) {
165 if (this.behavior
& RECREATE
) {
166 entry
= entry
.recreate();
170 if (this.behavior
& THROWAVAIL
) {
171 this.throwAndNotify(entry
);
174 if (!(this.behavior
& WAITFORWRITE
)) {
178 if (!(this.behavior
& PARTIAL
)) {
180 entry
.getMetaDataElement("meto");
185 if (this.behavior
& DONTFILL
) {
186 Assert
.equal(false, this.behavior
& WAITFORWRITE
);
191 executeSoon(function () {
192 // emulate network latency
193 entry
.setMetaDataElement("meto", self
.workingMetadata
);
194 entry
.metaDataReady();
195 if (self
.behavior
& METAONLY
) {
196 // Since forcing GC/CC doesn't trigger OnWriterClosed, we have to set the entry valid manually :(
197 if (!(self
.behavior
& DONTSETVALID
)) {
201 if (self
.behavior
& WAITFORWRITE
) {
207 executeSoon(function () {
208 // emulate more network latency
209 if (self
.behavior
& DOOMED
) {
210 LOG_C2(self
, "checking doom state");
212 let os
= entry
.openOutputStream(0, -1);
213 // Unfortunately, in the undetermined state we cannot even check whether the entry
214 // is actually doomed or not.
216 Assert
.ok(!!(self
.behavior
& MAYBE_NEW
));
220 if (self
.behavior
& WAITFORWRITE
) {
226 var offset
= self
.behavior
& PARTIAL
? entry
.dataSize
: 0;
227 LOG_C2(self
, "openOutputStream @ " + offset
);
228 let os
= entry
.openOutputStream(offset
, -1);
229 LOG_C2(self
, "writing data");
230 var wrt
= os
.write(self
.workingData
, self
.workingData
.length
);
231 Assert
.equal(wrt
, self
.workingData
.length
);
233 if (self
.behavior
& WAITFORWRITE
) {
241 Assert
.equal(entry
.getMetaDataElement("meto"), this.workingMetadata
);
242 if (this.behavior
& THROWAVAIL
) {
243 this.throwAndNotify(entry
);
245 if (this.behavior
& NOTIFYBEFOREREAD
) {
246 this.goon(entry
, true);
250 pumpReadStream(entry
.openInputStream(0), function (data
) {
251 Assert
.equal(data
, self
.workingData
);
252 self
.onDataCheckPassed
= true;
253 LOG_C2(self
, "entry read done");
259 LOG_C2(this, "selfCheck");
261 Assert
.ok(this.onCheckPassed
|| this.behavior
& MAYBE_NEW
);
262 Assert
.ok(this.onAvailPassed
);
263 Assert
.ok(this.onDataCheckPassed
|| this.behavior
& MAYBE_NEW
);
265 throwAndNotify(entry
) {
266 LOG_C2(this, "Throwing");
268 executeSoon(function () {
269 LOG_C2(self
, "Notifying");
272 throw Components
.Exception("", Cr
.NS_ERROR_FAILURE
);
276 function OpenCallback(behavior
, workingMetadata
, workingData
, goon
) {
277 this.behavior
= behavior
;
278 this.workingMetadata
= workingMetadata
;
279 this.workingData
= workingData
;
282 (!!(behavior
& (NEW
| RECREATE
)) || !workingMetadata
) &&
283 !(behavior
& NOTVALID
);
284 this.onAvailPassed
= false;
285 this.onDataCheckPassed
=
286 !!(behavior
& (NEW
| RECREATE
| NOTWANTED
)) || !workingMetadata
;
287 callbacks
.push(this);
288 this.order
= callbacks
.length
;
291 VisitCallback
.prototype = {
292 QueryInterface
: ChromeUtils
.generateQI(["nsICacheStorageVisitor"]),
293 onCacheStorageInfo(num
, consumption
) {
294 LOG_C2(this, "onCacheStorageInfo: num=" + num
+ ", size=" + consumption
);
295 Assert
.equal(this.num
, num
);
296 Assert
.equal(this.consumption
, consumption
);
312 var key
= (aIdEnhance
? aIdEnhance
+ ":" : "") + aURI
.asciiSpec
;
313 LOG_C2(this, "onCacheEntryInfo: key=" + key
);
315 function findCacheIndex(element
) {
316 if (typeof element
=== "string") {
317 return element
=== key
;
318 } else if (typeof element
=== "object") {
320 element
.uri
=== key
&&
321 element
.lci
.isAnonymous
=== aInfo
.isAnonymous
&&
322 ChromeUtils
.isOriginAttributesEqual(
323 element
.lci
.originAttributes
,
324 aInfo
.originAttributes
332 Assert
.ok(!!this.entries
);
334 var index
= this.entries
.findIndex(findCacheIndex
);
335 Assert
.ok(index
> -1);
337 this.entries
.splice(index
, 1);
339 onCacheEntryVisitCompleted() {
340 LOG_C2(this, "onCacheEntryVisitCompleted");
342 Assert
.equal(this.entries
.length
, 0);
347 Assert
.ok(!!this.goon
);
348 var goon
= this.goon
;
353 Assert
.ok(!this.entries
|| !this.entries
.length
);
357 function VisitCallback(num
, consumption
, entries
, goon
) {
359 this.consumption
= consumption
;
360 this.entries
= entries
;
362 callbacks
.push(this);
363 this.order
= callbacks
.length
;
366 EvictionCallback
.prototype = {
367 QueryInterface
: ChromeUtils
.generateQI(["nsICacheEntryDoomCallback"]),
368 onCacheEntryDoomed(result
) {
369 Assert
.equal(this.expectedSuccess
, result
== Cr
.NS_OK
);
375 function EvictionCallback(success
, goon
) {
376 this.expectedSuccess
= success
;
378 callbacks
.push(this);
379 this.order
= callbacks
.length
;
382 MultipleCallbacks
.prototype = {
384 if (--this.pending
== 0) {
387 executeSoon(function () {
400 function MultipleCallbacks(number
, goon
, delayed
) {
401 this.pending
= number
;
403 this.delayed
= delayed
;
406 function wait_for_cache_index(continue_func
) {
407 // This callback will not fire before the index is in the ready state. nsICacheStorage.exists() will
408 // no longer throw after this point.
409 Services
.cache2
.asyncGetDiskConsumption({
410 onNetworkCacheDiskConsumption() {
413 // eslint-disable-next-line mozilla/use-chromeutils-generateqi
420 function finish_cache2_test() {
421 callbacks
.forEach(function (callback
) {
422 callback
.selfCheck();