/src/mozilla-central/dom/cache/CacheStorage.cpp
Line | Count | Source (jump to first uncovered line) |
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/CacheStorage.h" |
8 | | |
9 | | #include "mozilla/Unused.h" |
10 | | #include "mozilla/dom/CacheBinding.h" |
11 | | #include "mozilla/dom/CacheStorageBinding.h" |
12 | | #include "mozilla/dom/InternalRequest.h" |
13 | | #include "mozilla/dom/Promise.h" |
14 | | #include "mozilla/dom/Response.h" |
15 | | #include "mozilla/dom/cache/AutoUtils.h" |
16 | | #include "mozilla/dom/cache/Cache.h" |
17 | | #include "mozilla/dom/cache/CacheChild.h" |
18 | | #include "mozilla/dom/cache/CacheStorageChild.h" |
19 | | #include "mozilla/dom/cache/CacheWorkerHolder.h" |
20 | | #include "mozilla/dom/cache/PCacheChild.h" |
21 | | #include "mozilla/dom/cache/ReadStream.h" |
22 | | #include "mozilla/dom/cache/TypeUtils.h" |
23 | | #include "mozilla/dom/WorkerPrivate.h" |
24 | | #include "mozilla/ipc/BackgroundChild.h" |
25 | | #include "mozilla/ipc/BackgroundUtils.h" |
26 | | #include "mozilla/ipc/PBackgroundChild.h" |
27 | | #include "mozilla/ipc/PBackgroundSharedTypes.h" |
28 | | #include "mozilla/StaticPrefs.h" |
29 | | #include "nsContentUtils.h" |
30 | | #include "nsIDocument.h" |
31 | | #include "nsIGlobalObject.h" |
32 | | #include "nsIScriptSecurityManager.h" |
33 | | #include "nsURLParsers.h" |
34 | | |
35 | | namespace mozilla { |
36 | | namespace dom { |
37 | | namespace cache { |
38 | | |
39 | | using mozilla::Unused; |
40 | | using mozilla::ErrorResult; |
41 | | using mozilla::ipc::BackgroundChild; |
42 | | using mozilla::ipc::PBackgroundChild; |
43 | | using mozilla::ipc::IProtocol; |
44 | | using mozilla::ipc::PrincipalInfo; |
45 | | using mozilla::ipc::PrincipalToPrincipalInfo; |
46 | | |
47 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::CacheStorage); |
48 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::CacheStorage); |
49 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::CacheStorage, |
50 | | mGlobal); |
51 | | |
52 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage) |
53 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
54 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
55 | 0 | NS_INTERFACE_MAP_END |
56 | | |
57 | | // We cannot reference IPC types in a webidl binding implementation header. So |
58 | | // define this in the .cpp. |
59 | | struct CacheStorage::Entry final |
60 | | { |
61 | | RefPtr<Promise> mPromise; |
62 | | CacheOpArgs mArgs; |
63 | | // We cannot add the requests until after the actor is present. So store |
64 | | // the request data separately for now. |
65 | | RefPtr<InternalRequest> mRequest; |
66 | | }; |
67 | | |
68 | | namespace { |
69 | | |
70 | | bool |
71 | | IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled) |
72 | 0 | { |
73 | 0 | // Can happen on main thread or worker thread |
74 | 0 |
|
75 | 0 | if (aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { |
76 | 0 | return true; |
77 | 0 | } |
78 | 0 | |
79 | 0 | // Require a ContentPrincipal to avoid null principal, etc. |
80 | 0 | if (NS_WARN_IF(aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo)) { |
81 | 0 | return false; |
82 | 0 | } |
83 | 0 | |
84 | 0 | // If we're in testing mode, then don't do any more work to determing if |
85 | 0 | // the origin is trusted. We have to run some tests as http. |
86 | 0 | if (aTestingPrefEnabled) { |
87 | 0 | return true; |
88 | 0 | } |
89 | 0 | |
90 | 0 | // Now parse the scheme of the principal's origin. This is a short term |
91 | 0 | // method for determining "trust". In the long term we need to implement |
92 | 0 | // the full algorithm here: |
93 | 0 | // |
94 | 0 | // https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure |
95 | 0 | // |
96 | 0 | // TODO: Implement full secure setting algorithm. (bug 1177856) |
97 | 0 | |
98 | 0 | const nsCString& flatURL = aPrincipalInfo.get_ContentPrincipalInfo().spec(); |
99 | 0 | const char* url = flatURL.get(); |
100 | 0 |
|
101 | 0 | // off the main thread URL parsing using nsStdURLParser. |
102 | 0 | nsCOMPtr<nsIURLParser> urlParser = new nsStdURLParser(); |
103 | 0 |
|
104 | 0 | uint32_t schemePos; |
105 | 0 | int32_t schemeLen; |
106 | 0 | uint32_t authPos; |
107 | 0 | int32_t authLen; |
108 | 0 | nsresult rv = urlParser->ParseURL(url, flatURL.Length(), |
109 | 0 | &schemePos, &schemeLen, |
110 | 0 | &authPos, &authLen, |
111 | 0 | nullptr, nullptr); // ignore path |
112 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { return false; } |
113 | 0 | |
114 | 0 | nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen)); |
115 | 0 | if (scheme.LowerCaseEqualsLiteral("https") || |
116 | 0 | scheme.LowerCaseEqualsLiteral("file")) { |
117 | 0 | return true; |
118 | 0 | } |
119 | 0 | |
120 | 0 | uint32_t hostPos; |
121 | 0 | int32_t hostLen; |
122 | 0 |
|
123 | 0 | rv = urlParser->ParseAuthority(url + authPos, authLen, |
124 | 0 | nullptr, nullptr, // ignore username |
125 | 0 | nullptr, nullptr, // ignore password |
126 | 0 | &hostPos, &hostLen, |
127 | 0 | nullptr); // ignore port |
128 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { return false; } |
129 | 0 | |
130 | 0 | nsDependentCSubstring hostname(url + authPos + hostPos, hostLen); |
131 | 0 |
|
132 | 0 | return hostname.EqualsLiteral("localhost") || |
133 | 0 | hostname.EqualsLiteral("127.0.0.1") || |
134 | 0 | hostname.EqualsLiteral("::1"); |
135 | 0 | } |
136 | | |
137 | | } // namespace |
138 | | |
139 | | // static |
140 | | already_AddRefed<CacheStorage> |
141 | | CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal, |
142 | | nsIPrincipal* aPrincipal, |
143 | | bool aForceTrustedOrigin, ErrorResult& aRv) |
144 | 0 | { |
145 | 0 | MOZ_DIAGNOSTIC_ASSERT(aGlobal); |
146 | 0 | MOZ_DIAGNOSTIC_ASSERT(aPrincipal); |
147 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
148 | 0 |
|
149 | 0 | PrincipalInfo principalInfo; |
150 | 0 | nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); |
151 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
152 | 0 | aRv.Throw(rv); |
153 | 0 | return nullptr; |
154 | 0 | } |
155 | 0 | |
156 | 0 | bool testingEnabled = aForceTrustedOrigin || |
157 | 0 | Preferences::GetBool("dom.caches.testing.enabled", false) || |
158 | 0 | StaticPrefs::dom_serviceWorkers_testing_enabled(); |
159 | 0 |
|
160 | 0 | if (!IsTrusted(principalInfo, testingEnabled)) { |
161 | 0 | NS_WARNING("CacheStorage not supported on untrusted origins."); |
162 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); |
163 | 0 | return ref.forget(); |
164 | 0 | } |
165 | 0 |
|
166 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal, |
167 | 0 | principalInfo, nullptr); |
168 | 0 | return ref.forget(); |
169 | 0 | } |
170 | | |
171 | | // static |
172 | | already_AddRefed<CacheStorage> |
173 | | CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal, |
174 | | WorkerPrivate* aWorkerPrivate, ErrorResult& aRv) |
175 | 0 | { |
176 | 0 | MOZ_DIAGNOSTIC_ASSERT(aGlobal); |
177 | 0 | MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); |
178 | 0 | aWorkerPrivate->AssertIsOnWorkerThread(); |
179 | 0 |
|
180 | 0 | if (aWorkerPrivate->GetOriginAttributes().mPrivateBrowsingId > 0) { |
181 | 0 | NS_WARNING("CacheStorage not supported during private browsing."); |
182 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); |
183 | 0 | return ref.forget(); |
184 | 0 | } |
185 | 0 |
|
186 | 0 | RefPtr<CacheWorkerHolder> workerHolder = |
187 | 0 | CacheWorkerHolder::Create(aWorkerPrivate, |
188 | 0 | CacheWorkerHolder::AllowIdleShutdownStart); |
189 | 0 | if (!workerHolder) { |
190 | 0 | NS_WARNING("Worker thread is shutting down."); |
191 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
192 | 0 | return nullptr; |
193 | 0 | } |
194 | 0 |
|
195 | 0 | const PrincipalInfo& principalInfo = aWorkerPrivate->GetPrincipalInfo(); |
196 | 0 |
|
197 | 0 | // We have a number of cases where we want to skip the https scheme |
198 | 0 | // validation: |
199 | 0 | // |
200 | 0 | // 1) Any worker when dom.caches.testing.enabled pref is true. |
201 | 0 | // 2) Any worker when dom.serviceWorkers.testing.enabled pref is true. This |
202 | 0 | // is mainly because most sites using SWs will expect Cache to work if |
203 | 0 | // SWs are enabled. |
204 | 0 | // 3) If the window that created this worker has the devtools SW testing |
205 | 0 | // option enabled. Same reasoning as (2). |
206 | 0 | // 4) If the worker itself is a ServiceWorker, then we always skip the |
207 | 0 | // origin checks. The ServiceWorker has its own trusted origin checks |
208 | 0 | // that are better than ours. In addition, we don't have information |
209 | 0 | // about the window any more, so we can't do our own checks. |
210 | 0 | bool testingEnabled = StaticPrefs::dom_caches_testing_enabled() || |
211 | 0 | StaticPrefs::dom_serviceWorkers_testing_enabled() || |
212 | 0 | aWorkerPrivate->ServiceWorkersTestingInWindow() || |
213 | 0 | aWorkerPrivate->IsServiceWorker(); |
214 | 0 |
|
215 | 0 | if (!IsTrusted(principalInfo, testingEnabled)) { |
216 | 0 | NS_WARNING("CacheStorage not supported on untrusted origins."); |
217 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); |
218 | 0 | return ref.forget(); |
219 | 0 | } |
220 | 0 |
|
221 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal, |
222 | 0 | principalInfo, workerHolder); |
223 | 0 | return ref.forget(); |
224 | 0 | } |
225 | | |
226 | | // static |
227 | | bool |
228 | | CacheStorage::DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal) |
229 | 0 | { |
230 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
231 | 0 | MOZ_DIAGNOSTIC_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, |
232 | 0 | "Passed object is not a global object!"); |
233 | 0 | js::AssertSameCompartment(aCx, aGlobal); |
234 | 0 |
|
235 | 0 | if (NS_WARN_IF(!CacheStorage_Binding::GetConstructorObject(aCx) || |
236 | 0 | !Cache_Binding::GetConstructorObject(aCx))) { |
237 | 0 | return false; |
238 | 0 | } |
239 | 0 | |
240 | 0 | nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal); |
241 | 0 | MOZ_DIAGNOSTIC_ASSERT(principal); |
242 | 0 |
|
243 | 0 | ErrorResult rv; |
244 | 0 | RefPtr<CacheStorage> storage = |
245 | 0 | CreateOnMainThread(DEFAULT_NAMESPACE, xpc::NativeGlobal(aGlobal), principal, |
246 | 0 | true, /* force trusted */ |
247 | 0 | rv); |
248 | 0 | if (NS_WARN_IF(rv.MaybeSetPendingException(aCx))) { |
249 | 0 | return false; |
250 | 0 | } |
251 | 0 | |
252 | 0 | JS::Rooted<JS::Value> caches(aCx); |
253 | 0 | if (NS_WARN_IF(!ToJSValue(aCx, storage, &caches))) { |
254 | 0 | return false; |
255 | 0 | } |
256 | 0 | |
257 | 0 | return JS_DefineProperty(aCx, aGlobal, "caches", caches, JSPROP_ENUMERATE); |
258 | 0 | } |
259 | | |
260 | | CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal, |
261 | | const PrincipalInfo& aPrincipalInfo, |
262 | | CacheWorkerHolder* aWorkerHolder) |
263 | | : mNamespace(aNamespace) |
264 | | , mGlobal(aGlobal) |
265 | | , mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo)) |
266 | | , mActor(nullptr) |
267 | | , mStatus(NS_OK) |
268 | 0 | { |
269 | 0 | MOZ_DIAGNOSTIC_ASSERT(mGlobal); |
270 | 0 |
|
271 | 0 | // If the PBackground actor is already initialized then we can |
272 | 0 | // immediately use it |
273 | 0 | PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread(); |
274 | 0 | if (NS_WARN_IF(!actor)) { |
275 | 0 | mStatus = NS_ERROR_UNEXPECTED; |
276 | 0 | return; |
277 | 0 | } |
278 | 0 | |
279 | 0 | // WorkerHolder ownership is passed to the CacheStorageChild actor and any |
280 | 0 | // actors it may create. The WorkerHolder will keep the worker thread alive |
281 | 0 | // until the actors can gracefully shutdown. |
282 | 0 | CacheStorageChild* newActor = new CacheStorageChild(this, aWorkerHolder); |
283 | 0 | PCacheStorageChild* constructedActor = |
284 | 0 | actor->SendPCacheStorageConstructor(newActor, mNamespace, *mPrincipalInfo); |
285 | 0 |
|
286 | 0 | if (NS_WARN_IF(!constructedActor)) { |
287 | 0 | mStatus = NS_ERROR_UNEXPECTED; |
288 | 0 | return; |
289 | 0 | } |
290 | 0 | |
291 | 0 | MOZ_DIAGNOSTIC_ASSERT(constructedActor == newActor); |
292 | 0 | mActor = newActor; |
293 | 0 | } |
294 | | |
295 | | CacheStorage::CacheStorage(nsresult aFailureResult) |
296 | | : mNamespace(INVALID_NAMESPACE) |
297 | | , mActor(nullptr) |
298 | | , mStatus(aFailureResult) |
299 | 0 | { |
300 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mStatus)); |
301 | 0 | } |
302 | | |
303 | | already_AddRefed<Promise> |
304 | | CacheStorage::Match(JSContext* aCx, const RequestOrUSVString& aRequest, |
305 | | const CacheQueryOptions& aOptions, ErrorResult& aRv) |
306 | 0 | { |
307 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
308 | 0 |
|
309 | 0 | if (!HasStorageAccess()) { |
310 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
311 | 0 | return nullptr; |
312 | 0 | } |
313 | 0 | |
314 | 0 | if (NS_WARN_IF(NS_FAILED(mStatus))) { |
315 | 0 | aRv.Throw(mStatus); |
316 | 0 | return nullptr; |
317 | 0 | } |
318 | 0 | |
319 | 0 | RefPtr<InternalRequest> request = |
320 | 0 | ToInternalRequest(aCx, aRequest, IgnoreBody, aRv); |
321 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
322 | 0 | return nullptr; |
323 | 0 | } |
324 | 0 | |
325 | 0 | RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); |
326 | 0 | if (NS_WARN_IF(!promise)) { |
327 | 0 | return nullptr; |
328 | 0 | } |
329 | 0 | |
330 | 0 | CacheQueryParams params; |
331 | 0 | ToCacheQueryParams(params, aOptions); |
332 | 0 |
|
333 | 0 | nsAutoPtr<Entry> entry(new Entry()); |
334 | 0 | entry->mPromise = promise; |
335 | 0 | entry->mArgs = StorageMatchArgs(CacheRequest(), params, GetOpenMode()); |
336 | 0 | entry->mRequest = request; |
337 | 0 |
|
338 | 0 | RunRequest(std::move(entry)); |
339 | 0 |
|
340 | 0 | return promise.forget(); |
341 | 0 | } |
342 | | |
343 | | already_AddRefed<Promise> |
344 | | CacheStorage::Has(const nsAString& aKey, ErrorResult& aRv) |
345 | 0 | { |
346 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
347 | 0 |
|
348 | 0 | if (!HasStorageAccess()) { |
349 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
350 | 0 | return nullptr; |
351 | 0 | } |
352 | 0 | |
353 | 0 | if (NS_WARN_IF(NS_FAILED(mStatus))) { |
354 | 0 | aRv.Throw(mStatus); |
355 | 0 | return nullptr; |
356 | 0 | } |
357 | 0 | |
358 | 0 | RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); |
359 | 0 | if (NS_WARN_IF(!promise)) { |
360 | 0 | return nullptr; |
361 | 0 | } |
362 | 0 | |
363 | 0 | nsAutoPtr<Entry> entry(new Entry()); |
364 | 0 | entry->mPromise = promise; |
365 | 0 | entry->mArgs = StorageHasArgs(nsString(aKey)); |
366 | 0 |
|
367 | 0 | RunRequest(std::move(entry)); |
368 | 0 |
|
369 | 0 | return promise.forget(); |
370 | 0 | } |
371 | | |
372 | | already_AddRefed<Promise> |
373 | | CacheStorage::Open(const nsAString& aKey, ErrorResult& aRv) |
374 | 0 | { |
375 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
376 | 0 |
|
377 | 0 | if (!HasStorageAccess()) { |
378 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
379 | 0 | return nullptr; |
380 | 0 | } |
381 | 0 | |
382 | 0 | if (NS_WARN_IF(NS_FAILED(mStatus))) { |
383 | 0 | aRv.Throw(mStatus); |
384 | 0 | return nullptr; |
385 | 0 | } |
386 | 0 | |
387 | 0 | RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); |
388 | 0 | if (NS_WARN_IF(!promise)) { |
389 | 0 | return nullptr; |
390 | 0 | } |
391 | 0 | |
392 | 0 | nsAutoPtr<Entry> entry(new Entry()); |
393 | 0 | entry->mPromise = promise; |
394 | 0 | entry->mArgs = StorageOpenArgs(nsString(aKey)); |
395 | 0 |
|
396 | 0 | RunRequest(std::move(entry)); |
397 | 0 |
|
398 | 0 | return promise.forget(); |
399 | 0 | } |
400 | | |
401 | | already_AddRefed<Promise> |
402 | | CacheStorage::Delete(const nsAString& aKey, ErrorResult& aRv) |
403 | 0 | { |
404 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
405 | 0 |
|
406 | 0 | if (!HasStorageAccess()) { |
407 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
408 | 0 | return nullptr; |
409 | 0 | } |
410 | 0 | |
411 | 0 | if (NS_WARN_IF(NS_FAILED(mStatus))) { |
412 | 0 | aRv.Throw(mStatus); |
413 | 0 | return nullptr; |
414 | 0 | } |
415 | 0 | |
416 | 0 | RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); |
417 | 0 | if (NS_WARN_IF(!promise)) { |
418 | 0 | return nullptr; |
419 | 0 | } |
420 | 0 | |
421 | 0 | nsAutoPtr<Entry> entry(new Entry()); |
422 | 0 | entry->mPromise = promise; |
423 | 0 | entry->mArgs = StorageDeleteArgs(nsString(aKey)); |
424 | 0 |
|
425 | 0 | RunRequest(std::move(entry)); |
426 | 0 |
|
427 | 0 | return promise.forget(); |
428 | 0 | } |
429 | | |
430 | | already_AddRefed<Promise> |
431 | | CacheStorage::Keys(ErrorResult& aRv) |
432 | 0 | { |
433 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
434 | 0 |
|
435 | 0 | if (!HasStorageAccess()) { |
436 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
437 | 0 | return nullptr; |
438 | 0 | } |
439 | 0 | |
440 | 0 | if (NS_WARN_IF(NS_FAILED(mStatus))) { |
441 | 0 | aRv.Throw(mStatus); |
442 | 0 | return nullptr; |
443 | 0 | } |
444 | 0 | |
445 | 0 | RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); |
446 | 0 | if (NS_WARN_IF(!promise)) { |
447 | 0 | return nullptr; |
448 | 0 | } |
449 | 0 | |
450 | 0 | nsAutoPtr<Entry> entry(new Entry()); |
451 | 0 | entry->mPromise = promise; |
452 | 0 | entry->mArgs = StorageKeysArgs(); |
453 | 0 |
|
454 | 0 | RunRequest(std::move(entry)); |
455 | 0 |
|
456 | 0 | return promise.forget(); |
457 | 0 | } |
458 | | |
459 | | // static |
460 | | already_AddRefed<CacheStorage> |
461 | | CacheStorage::Constructor(const GlobalObject& aGlobal, |
462 | | CacheStorageNamespace aNamespace, |
463 | | nsIPrincipal* aPrincipal, ErrorResult& aRv) |
464 | 0 | { |
465 | 0 | if (NS_WARN_IF(!NS_IsMainThread())) { |
466 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
467 | 0 | return nullptr; |
468 | 0 | } |
469 | 0 | |
470 | 0 | // TODO: remove Namespace in favor of CacheStorageNamespace |
471 | 0 | static_assert(DEFAULT_NAMESPACE == (uint32_t)CacheStorageNamespace::Content, |
472 | 0 | "Default namespace should match webidl Content enum"); |
473 | 0 | static_assert(CHROME_ONLY_NAMESPACE == (uint32_t)CacheStorageNamespace::Chrome, |
474 | 0 | "Chrome namespace should match webidl Chrome enum"); |
475 | 0 | static_assert(NUMBER_OF_NAMESPACES == (uint32_t)CacheStorageNamespace::EndGuard_, |
476 | 0 | "Number of namespace should match webidl endguard enum"); |
477 | 0 |
|
478 | 0 | Namespace ns = static_cast<Namespace>(aNamespace); |
479 | 0 | nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); |
480 | 0 |
|
481 | 0 | bool privateBrowsing = false; |
482 | 0 | if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global)) { |
483 | 0 | nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); |
484 | 0 | if (doc) { |
485 | 0 | nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext(); |
486 | 0 | privateBrowsing = loadContext && loadContext->UsePrivateBrowsing(); |
487 | 0 | } |
488 | 0 | } |
489 | 0 |
|
490 | 0 | if (privateBrowsing) { |
491 | 0 | RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); |
492 | 0 | return ref.forget(); |
493 | 0 | } |
494 | 0 | |
495 | 0 | // Create a CacheStorage object bypassing the trusted origin checks |
496 | 0 | // since this is a chrome-only constructor. |
497 | 0 | return CreateOnMainThread(ns, global, aPrincipal, |
498 | 0 | true /* force trusted origin */, aRv); |
499 | 0 | } |
500 | | |
501 | | nsISupports* |
502 | | CacheStorage::GetParentObject() const |
503 | 0 | { |
504 | 0 | return mGlobal; |
505 | 0 | } |
506 | | |
507 | | JSObject* |
508 | | CacheStorage::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto) |
509 | 0 | { |
510 | 0 | return mozilla::dom::CacheStorage_Binding::Wrap(aContext, this, aGivenProto); |
511 | 0 | } |
512 | | |
513 | | void |
514 | | CacheStorage::DestroyInternal(CacheStorageChild* aActor) |
515 | 0 | { |
516 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
517 | 0 | MOZ_DIAGNOSTIC_ASSERT(mActor); |
518 | 0 | MOZ_DIAGNOSTIC_ASSERT(mActor == aActor); |
519 | 0 | MOZ_DIAGNOSTIC_ASSERT(!NS_FAILED(mStatus)); |
520 | 0 | mActor->ClearListener(); |
521 | 0 | mActor = nullptr; |
522 | 0 | mStatus = NS_ERROR_UNEXPECTED; |
523 | 0 |
|
524 | 0 | // Note that we will never get an actor again in case another request is |
525 | 0 | // made before this object is destructed. |
526 | 0 | } |
527 | | |
528 | | nsIGlobalObject* |
529 | | CacheStorage::GetGlobalObject() const |
530 | 0 | { |
531 | 0 | return mGlobal; |
532 | 0 | } |
533 | | |
534 | | #ifdef DEBUG |
535 | | void |
536 | | CacheStorage::AssertOwningThread() const |
537 | | { |
538 | | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
539 | | } |
540 | | #endif |
541 | | |
542 | | PBackgroundChild* |
543 | | CacheStorage::GetIPCManager() |
544 | 0 | { |
545 | 0 | // This is true because CacheStorage always uses IgnoreBody for requests. |
546 | 0 | // So we should never need to get the IPC manager during Request or |
547 | 0 | // Response serialization. |
548 | 0 | MOZ_CRASH("CacheStorage does not implement TypeUtils::GetIPCManager()"); |
549 | 0 | } |
550 | | |
551 | | CacheStorage::~CacheStorage() |
552 | 0 | { |
553 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
554 | 0 | if (mActor) { |
555 | 0 | mActor->StartDestroyFromListener(); |
556 | 0 | // DestroyInternal() is called synchronously by StartDestroyFromListener(). |
557 | 0 | // So we should have already cleared the mActor. |
558 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mActor); |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | void |
563 | | CacheStorage::RunRequest(nsAutoPtr<Entry>&& aEntry) |
564 | 0 | { |
565 | 0 | MOZ_ASSERT(mActor); |
566 | 0 |
|
567 | 0 | nsAutoPtr<Entry> entry(std::move(aEntry)); |
568 | 0 |
|
569 | 0 | AutoChildOpArgs args(this, entry->mArgs, 1); |
570 | 0 |
|
571 | 0 | if (entry->mRequest) { |
572 | 0 | ErrorResult rv; |
573 | 0 | args.Add(entry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv); |
574 | 0 | if (NS_WARN_IF(rv.Failed())) { |
575 | 0 | entry->mPromise->MaybeReject(rv); |
576 | 0 | return; |
577 | 0 | } |
578 | 0 | } |
579 | 0 | |
580 | 0 | mActor->ExecuteOp(mGlobal, entry->mPromise, this, args.SendAsOpArgs()); |
581 | 0 | } |
582 | | |
583 | | OpenMode |
584 | | CacheStorage::GetOpenMode() const |
585 | 0 | { |
586 | 0 | return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy; |
587 | 0 | } |
588 | | |
589 | | bool |
590 | | CacheStorage::HasStorageAccess() const |
591 | 0 | { |
592 | 0 | NS_ASSERT_OWNINGTHREAD(CacheStorage); |
593 | 0 |
|
594 | 0 | if (NS_IsMainThread()) { |
595 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal); |
596 | 0 | if (NS_WARN_IF(!window)) { |
597 | 0 | return true; |
598 | 0 | } |
599 | 0 | |
600 | 0 | nsContentUtils::StorageAccess access = |
601 | 0 | nsContentUtils::StorageAllowedForWindow(window); |
602 | 0 | return access > nsContentUtils::StorageAccess::ePrivateBrowsing; |
603 | 0 | } |
604 | 0 | |
605 | 0 | WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); |
606 | 0 | MOZ_ASSERT(workerPrivate); |
607 | 0 |
|
608 | 0 | return workerPrivate->IsStorageAllowed(); |
609 | 0 | } |
610 | | |
611 | | } // namespace cache |
612 | | } // namespace dom |
613 | | } // namespace mozilla |