Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/cache/AutoUtils.h"
8 :
9 : #include "mozilla/Unused.h"
10 : #include "mozilla/dom/InternalHeaders.h"
11 : #include "mozilla/dom/InternalRequest.h"
12 : #include "mozilla/dom/cache/CacheParent.h"
13 : #include "mozilla/dom/cache/CacheStreamControlParent.h"
14 : #include "mozilla/dom/cache/ReadStream.h"
15 : #include "mozilla/dom/cache/SavedTypes.h"
16 : #include "mozilla/dom/cache/StreamList.h"
17 : #include "mozilla/dom/cache/TypeUtils.h"
18 : #include "mozilla/ipc/IPCStreamUtils.h"
19 : #include "mozilla/ipc/PBackgroundParent.h"
20 : #include "nsCRT.h"
21 : #include "nsHttp.h"
22 :
23 : using mozilla::Unused;
24 : using mozilla::dom::cache::CacheReadStream;
25 : using mozilla::dom::cache::CacheReadStreamOrVoid;
26 : using mozilla::ipc::AutoIPCStream;
27 : using mozilla::ipc::PBackgroundParent;
28 :
29 : namespace {
30 :
31 : enum CleanupAction
32 : {
33 : Forget,
34 : Delete
35 : };
36 :
37 : void
38 0 : CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction)
39 : {
40 : // fds cleaned up by mStreamCleanupList
41 : // PChildToParentStream actors cleaned up by mStreamCleanupList
42 0 : }
43 :
44 : void
45 0 : CleanupChild(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
46 : {
47 0 : if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
48 0 : return;
49 : }
50 :
51 0 : CleanupChild(aReadStreamOrVoid.get_CacheReadStream(), aAction);
52 : }
53 :
54 : } // namespace
55 :
56 : namespace mozilla {
57 : namespace dom {
58 : namespace cache {
59 :
60 : // --------------------------------------------
61 :
62 0 : AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
63 : const CacheOpArgs& aOpArgs,
64 0 : uint32_t aEntryCount)
65 : : mTypeUtils(aTypeUtils)
66 : , mOpArgs(aOpArgs)
67 0 : , mSent(false)
68 : {
69 0 : MOZ_DIAGNOSTIC_ASSERT(mTypeUtils);
70 0 : MOZ_RELEASE_ASSERT(aEntryCount != 0);
71 : // We are using AutoIPCStream objects to cleanup target IPCStream
72 : // structures embedded in our CacheOpArgs. These IPCStream structs
73 : // must not move once we attach our AutoIPCStream to them. Therefore,
74 : // its important that any arrays containing streams are pre-sized for
75 : // the number of entries we have in order to avoid realloc moving
76 : // things around on us.
77 0 : if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
78 0 : CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
79 0 : args.requestResponseList().SetCapacity(aEntryCount);
80 : } else {
81 0 : MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
82 : }
83 0 : }
84 :
85 0 : AutoChildOpArgs::~AutoChildOpArgs()
86 : {
87 0 : CleanupAction action = mSent ? Forget : Delete;
88 :
89 0 : switch(mOpArgs.type()) {
90 : case CacheOpArgs::TCacheMatchArgs:
91 : {
92 0 : CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
93 0 : CleanupChild(args.request().body(), action);
94 0 : break;
95 : }
96 : case CacheOpArgs::TCacheMatchAllArgs:
97 : {
98 0 : CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
99 0 : if (args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t) {
100 0 : break;
101 : }
102 0 : CleanupChild(args.requestOrVoid().get_CacheRequest().body(), action);
103 0 : break;
104 : }
105 : case CacheOpArgs::TCachePutAllArgs:
106 : {
107 0 : CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
108 0 : auto& list = args.requestResponseList();
109 0 : for (uint32_t i = 0; i < list.Length(); ++i) {
110 0 : CleanupChild(list[i].request().body(), action);
111 0 : CleanupChild(list[i].response().body(), action);
112 : }
113 0 : break;
114 : }
115 : case CacheOpArgs::TCacheDeleteArgs:
116 : {
117 0 : CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
118 0 : CleanupChild(args.request().body(), action);
119 0 : break;
120 : }
121 : case CacheOpArgs::TCacheKeysArgs:
122 : {
123 0 : CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
124 0 : if (args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t) {
125 0 : break;
126 : }
127 0 : CleanupChild(args.requestOrVoid().get_CacheRequest().body(), action);
128 0 : break;
129 : }
130 : case CacheOpArgs::TStorageMatchArgs:
131 : {
132 0 : StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
133 0 : CleanupChild(args.request().body(), action);
134 0 : break;
135 : }
136 : default:
137 : // Other types do not need cleanup
138 0 : break;
139 : }
140 :
141 0 : mStreamCleanupList.Clear();
142 0 : }
143 :
144 : void
145 0 : AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
146 : SchemeAction aSchemeAction, ErrorResult& aRv)
147 : {
148 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
149 :
150 0 : switch(mOpArgs.type()) {
151 : case CacheOpArgs::TCacheMatchArgs:
152 : {
153 0 : CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
154 0 : mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
155 0 : aSchemeAction, mStreamCleanupList, aRv);
156 0 : break;
157 : }
158 : case CacheOpArgs::TCacheMatchAllArgs:
159 : {
160 0 : CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
161 0 : MOZ_DIAGNOSTIC_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
162 0 : args.requestOrVoid() = CacheRequest();
163 0 : mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
164 : aRequest, aBodyAction, aSchemeAction,
165 0 : mStreamCleanupList, aRv);
166 0 : break;
167 : }
168 : case CacheOpArgs::TCacheDeleteArgs:
169 : {
170 0 : CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
171 0 : mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
172 0 : aSchemeAction, mStreamCleanupList, aRv);
173 0 : break;
174 : }
175 : case CacheOpArgs::TCacheKeysArgs:
176 : {
177 0 : CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
178 0 : MOZ_DIAGNOSTIC_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
179 0 : args.requestOrVoid() = CacheRequest();
180 0 : mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
181 : aRequest, aBodyAction, aSchemeAction,
182 0 : mStreamCleanupList, aRv);
183 0 : break;
184 : }
185 : case CacheOpArgs::TStorageMatchArgs:
186 : {
187 0 : StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
188 0 : mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
189 0 : aSchemeAction, mStreamCleanupList, aRv);
190 0 : break;
191 : }
192 : default:
193 0 : MOZ_CRASH("Cache args type cannot send a Request!");
194 : }
195 0 : }
196 :
197 : namespace {
198 :
199 : bool
200 0 : MatchInPutList(InternalRequest* aRequest,
201 : const nsTArray<CacheRequestResponse>& aPutList)
202 : {
203 0 : MOZ_DIAGNOSTIC_ASSERT(aRequest);
204 :
205 : // This method implements the SW spec QueryCache algorithm against an
206 : // in memory array of Request/Response objects. This essentially the
207 : // same algorithm that is implemented in DBSchema.cpp. Unfortunately
208 : // we cannot unify them because when operating against the real database
209 : // we don't want to load all request/response objects into memory.
210 :
211 : // Note, we can skip the check for a invalid request method because
212 : // Cache should only call into here with a GET or HEAD.
213 : #ifdef DEBUG
214 0 : nsAutoCString method;
215 0 : aRequest->GetMethod(method);
216 0 : MOZ_ASSERT(method.LowerCaseEqualsLiteral("get") ||
217 : method.LowerCaseEqualsLiteral("head"));
218 : #endif
219 :
220 0 : RefPtr<InternalHeaders> requestHeaders = aRequest->Headers();
221 :
222 0 : for (uint32_t i = 0; i < aPutList.Length(); ++i) {
223 0 : const CacheRequest& cachedRequest = aPutList[i].request();
224 0 : const CacheResponse& cachedResponse = aPutList[i].response();
225 :
226 0 : nsAutoCString url;
227 0 : aRequest->GetURL(url);
228 :
229 0 : nsAutoCString requestUrl(cachedRequest.urlWithoutQuery());
230 0 : requestUrl.Append(cachedRequest.urlQuery());
231 :
232 : // If the URLs don't match, then just skip to the next entry.
233 0 : if (url != requestUrl) {
234 0 : continue;
235 : }
236 :
237 : RefPtr<InternalHeaders> cachedRequestHeaders =
238 0 : TypeUtils::ToInternalHeaders(cachedRequest.headers());
239 :
240 : RefPtr<InternalHeaders> cachedResponseHeaders =
241 0 : TypeUtils::ToInternalHeaders(cachedResponse.headers());
242 :
243 0 : nsCString varyHeaders;
244 0 : ErrorResult rv;
245 0 : cachedResponseHeaders->Get(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
246 0 : MOZ_ALWAYS_TRUE(!rv.Failed());
247 :
248 : // Assume the vary headers match until we find a conflict
249 0 : bool varyHeadersMatch = true;
250 :
251 0 : char* rawBuffer = varyHeaders.BeginWriting();
252 0 : char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
253 0 : for (; token;
254 0 : token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
255 0 : nsDependentCString header(token);
256 0 : MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
257 : "We should have already caught this in "
258 : "TypeUtils::ToPCacheResponseWithoutBody()");
259 :
260 0 : ErrorResult headerRv;
261 0 : nsAutoCString value;
262 0 : requestHeaders->Get(header, value, headerRv);
263 0 : if (NS_WARN_IF(headerRv.Failed())) {
264 0 : headerRv.SuppressException();
265 0 : MOZ_DIAGNOSTIC_ASSERT(value.IsEmpty());
266 : }
267 :
268 0 : nsAutoCString cachedValue;
269 0 : cachedRequestHeaders->Get(header, cachedValue, headerRv);
270 0 : if (NS_WARN_IF(headerRv.Failed())) {
271 0 : headerRv.SuppressException();
272 0 : MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
273 : }
274 :
275 0 : if (value != cachedValue) {
276 0 : varyHeadersMatch = false;
277 0 : break;
278 : }
279 : }
280 :
281 : // URL was equal and all vary headers match!
282 0 : if (varyHeadersMatch) {
283 0 : return true;
284 : }
285 : }
286 :
287 0 : return false;
288 : }
289 :
290 : } // namespace
291 :
292 : void
293 0 : AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
294 : SchemeAction aSchemeAction, Response& aResponse,
295 : ErrorResult& aRv)
296 : {
297 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
298 :
299 0 : switch(mOpArgs.type()) {
300 : case CacheOpArgs::TCachePutAllArgs:
301 : {
302 0 : CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
303 :
304 : // Throw an error if a request/response pair would mask another
305 : // request/response pair in the same PutAll operation. This is
306 : // step 2.3.2.3 from the "Batch Cache Operations" spec algorithm.
307 0 : if (MatchInPutList(aRequest, args.requestResponseList())) {
308 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
309 0 : return;
310 : }
311 :
312 : // Ensure that we don't realloc the array since this can result
313 : // in our AutoIPCStream objects to reference the wrong memory
314 : // location. This should never happen and is a UAF if it does.
315 : // Therefore make this a release assertion.
316 0 : MOZ_RELEASE_ASSERT(args.requestResponseList().Length() <
317 : args.requestResponseList().Capacity());
318 :
319 : // The FileDescriptorSetChild asserts in its destructor that all fds have
320 : // been removed. The copy constructor, however, simply duplicates the
321 : // fds without removing any. This means each temporary and copy must be
322 : // explicitly cleaned up.
323 : //
324 : // Avoid a lot of this hassle by making sure we only create one here. On
325 : // error we remove it.
326 0 : CacheRequestResponse& pair = *args.requestResponseList().AppendElement();
327 0 : pair.request().body() = void_t();
328 0 : pair.response().body() = void_t();
329 :
330 0 : mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
331 0 : aSchemeAction, mStreamCleanupList, aRv);
332 0 : if (!aRv.Failed()) {
333 0 : mTypeUtils->ToCacheResponse(pair.response(), aResponse,
334 0 : mStreamCleanupList, aRv);
335 : }
336 :
337 0 : if (aRv.Failed()) {
338 0 : CleanupChild(pair.request().body(), Delete);
339 0 : args.requestResponseList().RemoveElementAt(
340 0 : args.requestResponseList().Length() - 1);
341 : }
342 :
343 0 : break;
344 : }
345 : default:
346 0 : MOZ_CRASH("Cache args type cannot send a Request/Response pair!");
347 : }
348 : }
349 :
350 : const CacheOpArgs&
351 0 : AutoChildOpArgs::SendAsOpArgs()
352 : {
353 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
354 0 : mSent = true;
355 0 : for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
356 0 : autoStream->TakeValue();
357 : }
358 0 : return mOpArgs;
359 : }
360 :
361 : // --------------------------------------------
362 :
363 0 : AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
364 : const CacheOpResult& aOpResult,
365 0 : uint32_t aEntryCount)
366 : : mManager(aManager)
367 : , mOpResult(aOpResult)
368 : , mStreamControl(nullptr)
369 0 : , mSent(false)
370 : {
371 0 : MOZ_DIAGNOSTIC_ASSERT(mManager);
372 0 : MOZ_RELEASE_ASSERT(aEntryCount != 0);
373 : // We are using AutoIPCStream objects to cleanup target IPCStream
374 : // structures embedded in our CacheOpArgs. These IPCStream structs
375 : // must not move once we attach our AutoIPCStream to them. Therefore,
376 : // its important that any arrays containing streams are pre-sized for
377 : // the number of entries we have in order to avoid realloc moving
378 : // things around on us.
379 0 : if (mOpResult.type() == CacheOpResult::TCacheMatchAllResult) {
380 0 : CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
381 0 : result.responseList().SetCapacity(aEntryCount);
382 0 : } else if (mOpResult.type() == CacheOpResult::TCacheKeysResult) {
383 0 : CacheKeysResult& result = mOpResult.get_CacheKeysResult();
384 0 : result.requestList().SetCapacity(aEntryCount);
385 : } else {
386 0 : MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
387 : }
388 0 : }
389 :
390 0 : AutoParentOpResult::~AutoParentOpResult()
391 : {
392 0 : CleanupAction action = mSent ? Forget : Delete;
393 :
394 0 : switch (mOpResult.type()) {
395 : case CacheOpResult::TStorageOpenResult:
396 : {
397 0 : StorageOpenResult& result = mOpResult.get_StorageOpenResult();
398 0 : if (action == Forget || result.actorParent() == nullptr) {
399 0 : break;
400 : }
401 0 : Unused << PCacheParent::Send__delete__(result.actorParent());
402 0 : break;
403 : }
404 : default:
405 : // other types do not need additional clean up
406 0 : break;
407 : }
408 :
409 0 : if (action == Delete && mStreamControl) {
410 0 : Unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
411 : }
412 :
413 0 : mStreamCleanupList.Clear();
414 0 : }
415 :
416 : void
417 0 : AutoParentOpResult::Add(CacheId aOpenedCacheId, Manager* aManager)
418 : {
419 0 : MOZ_DIAGNOSTIC_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
420 0 : MOZ_DIAGNOSTIC_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
421 0 : mOpResult.get_StorageOpenResult().actorParent() =
422 0 : mManager->SendPCacheConstructor(new CacheParent(aManager, aOpenedCacheId));
423 0 : }
424 :
425 : void
426 0 : AutoParentOpResult::Add(const SavedResponse& aSavedResponse,
427 : StreamList* aStreamList)
428 : {
429 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
430 :
431 0 : switch (mOpResult.type()) {
432 : case CacheOpResult::TCacheMatchResult:
433 : {
434 0 : CacheMatchResult& result = mOpResult.get_CacheMatchResult();
435 0 : MOZ_DIAGNOSTIC_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
436 0 : result.responseOrVoid() = aSavedResponse.mValue;
437 : SerializeResponseBody(aSavedResponse, aStreamList,
438 0 : &result.responseOrVoid().get_CacheResponse());
439 0 : break;
440 : }
441 : case CacheOpResult::TCacheMatchAllResult:
442 : {
443 0 : CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
444 : // Ensure that we don't realloc the array since this can result
445 : // in our AutoIPCStream objects to reference the wrong memory
446 : // location. This should never happen and is a UAF if it does.
447 : // Therefore make this a release assertion.
448 0 : MOZ_RELEASE_ASSERT(result.responseList().Length() <
449 : result.responseList().Capacity());
450 0 : result.responseList().AppendElement(aSavedResponse.mValue);
451 : SerializeResponseBody(aSavedResponse, aStreamList,
452 0 : &result.responseList().LastElement());
453 0 : break;
454 : }
455 : case CacheOpResult::TStorageMatchResult:
456 : {
457 0 : StorageMatchResult& result = mOpResult.get_StorageMatchResult();
458 0 : MOZ_DIAGNOSTIC_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
459 0 : result.responseOrVoid() = aSavedResponse.mValue;
460 : SerializeResponseBody(aSavedResponse, aStreamList,
461 0 : &result.responseOrVoid().get_CacheResponse());
462 0 : break;
463 : }
464 : default:
465 0 : MOZ_CRASH("Cache result type cannot handle returning a Response!");
466 : }
467 0 : }
468 :
469 : void
470 0 : AutoParentOpResult::Add(const SavedRequest& aSavedRequest,
471 : StreamList* aStreamList)
472 : {
473 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
474 :
475 0 : switch (mOpResult.type()) {
476 : case CacheOpResult::TCacheKeysResult:
477 : {
478 0 : CacheKeysResult& result = mOpResult.get_CacheKeysResult();
479 : // Ensure that we don't realloc the array since this can result
480 : // in our AutoIPCStream objects to reference the wrong memory
481 : // location. This should never happen and is a UAF if it does.
482 : // Therefore make this a release assertion.
483 0 : MOZ_RELEASE_ASSERT(result.requestList().Length() <
484 : result.requestList().Capacity());
485 0 : result.requestList().AppendElement(aSavedRequest.mValue);
486 0 : CacheRequest& request = result.requestList().LastElement();
487 :
488 0 : if (!aSavedRequest.mHasBodyId) {
489 0 : request.body() = void_t();
490 0 : break;
491 : }
492 :
493 0 : request.body() = CacheReadStream();
494 0 : SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
495 0 : &request.body().get_CacheReadStream());
496 0 : break;
497 : }
498 : default:
499 0 : MOZ_CRASH("Cache result type cannot handle returning a Request!");
500 : }
501 0 : }
502 :
503 : const CacheOpResult&
504 0 : AutoParentOpResult::SendAsOpResult()
505 : {
506 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
507 0 : mSent = true;
508 0 : for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
509 0 : autoStream->TakeValue();
510 : }
511 0 : return mOpResult;
512 : }
513 :
514 : void
515 0 : AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
516 : StreamList* aStreamList,
517 : CacheResponse* aResponseOut)
518 : {
519 0 : MOZ_DIAGNOSTIC_ASSERT(aResponseOut);
520 :
521 0 : if (!aSavedResponse.mHasBodyId) {
522 0 : aResponseOut->body() = void_t();
523 0 : return;
524 : }
525 :
526 0 : aResponseOut->body() = CacheReadStream();
527 0 : SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
528 0 : &aResponseOut->body().get_CacheReadStream());
529 : }
530 :
531 : void
532 0 : AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
533 : CacheReadStream* aReadStreamOut)
534 : {
535 0 : MOZ_DIAGNOSTIC_ASSERT(aStreamList);
536 0 : MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
537 0 : MOZ_DIAGNOSTIC_ASSERT(!mSent);
538 :
539 0 : nsCOMPtr<nsIInputStream> stream = aStreamList->Extract(aId);
540 0 : MOZ_DIAGNOSTIC_ASSERT(stream);
541 :
542 0 : if (!mStreamControl) {
543 0 : mStreamControl = static_cast<CacheStreamControlParent*>(
544 0 : mManager->SendPCacheStreamControlConstructor(new CacheStreamControlParent()));
545 :
546 : // If this failed, then the child process is gone. Warn and allow actor
547 : // cleanup to proceed as normal.
548 0 : if (!mStreamControl) {
549 0 : NS_WARNING("Cache failed to create stream control actor.");
550 0 : return;
551 : }
552 : }
553 :
554 0 : aStreamList->SetStreamControl(mStreamControl);
555 :
556 0 : RefPtr<ReadStream> readStream = ReadStream::Create(mStreamControl,
557 0 : aId, stream);
558 0 : ErrorResult rv;
559 0 : readStream->Serialize(aReadStreamOut, mStreamCleanupList, rv);
560 0 : MOZ_DIAGNOSTIC_ASSERT(!rv.Failed());
561 : }
562 :
563 : } // namespace cache
564 : } // namespace dom
565 : } // namespace mozilla
|