Line data Source code
1 : /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "OfflineCacheUpdateChild.h"
7 : #include "OfflineCacheUpdateParent.h"
8 : #include "nsXULAppAPI.h"
9 : #include "OfflineCacheUpdateGlue.h"
10 : #include "nsOfflineCacheUpdate.h"
11 :
12 : #include "nsCPrefetchService.h"
13 : #include "nsCURILoader.h"
14 : #include "nsIApplicationCacheContainer.h"
15 : #include "nsIApplicationCacheChannel.h"
16 : #include "nsIApplicationCacheService.h"
17 : #include "nsICachingChannel.h"
18 : #include "nsIContent.h"
19 : #include "nsIDocShell.h"
20 : #include "nsIDocumentLoader.h"
21 : #include "nsIDOMElement.h"
22 : #include "nsIDOMWindow.h"
23 : #include "nsIDOMOfflineResourceList.h"
24 : #include "nsIDocument.h"
25 : #include "nsIObserverService.h"
26 : #include "nsIURL.h"
27 : #include "nsIWebProgress.h"
28 : #include "nsIWebNavigation.h"
29 : #include "nsICryptoHash.h"
30 : #include "nsIPermissionManager.h"
31 : #include "nsIPrincipal.h"
32 : #include "nsNetCID.h"
33 : #include "nsServiceManagerUtils.h"
34 : #include "nsStreamUtils.h"
35 : #include "nsThreadUtils.h"
36 : #include "nsProxyRelease.h"
37 : #include "mozilla/Logging.h"
38 : #include "nsIAsyncVerifyRedirectCallback.h"
39 : #include "mozilla/Preferences.h"
40 : #include "mozilla/Attributes.h"
41 : #include "mozilla/SizePrintfMacros.h"
42 : #include "mozilla/Unused.h"
43 : #include "nsIDiskSpaceWatcher.h"
44 : #include "nsIDocShell.h"
45 : #include "nsIDocShellTreeItem.h"
46 : #include "nsIDocShellTreeOwner.h"
47 : #include "mozilla/dom/ContentChild.h"
48 : #include "mozilla/dom/PermissionMessageUtils.h"
49 : #include "nsContentUtils.h"
50 : #include "mozilla/Unused.h"
51 :
52 : using namespace mozilla;
53 : using namespace mozilla::dom;
54 :
55 : static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr;
56 : static bool sAllowOfflineCache = true;
57 :
58 : nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr;
59 :
60 1 : nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains()
61 : {
62 1 : if (!mAllowedDomains)
63 1 : mAllowedDomains = new nsTHashtable<nsCStringHashKey>();
64 :
65 1 : return mAllowedDomains;
66 : }
67 :
68 :
69 : typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent;
70 : typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild;
71 : typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue;
72 :
73 : //
74 : // To enable logging (see mozilla/Logging.h for full details):
75 : //
76 : // set MOZ_LOG=nsOfflineCacheUpdate:5
77 : // set MOZ_LOG_FILE=offlineupdate.log
78 : //
79 : // this enables LogLevel::Debug level information and places all output in
80 : // the file offlineupdate.log
81 : //
82 : LazyLogModule gOfflineCacheUpdateLog("nsOfflineCacheUpdate");
83 :
84 : #undef LOG
85 : #define LOG(args) MOZ_LOG(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug, args)
86 :
87 : #undef LOG_ENABLED
88 : #define LOG_ENABLED() MOZ_LOG_TEST(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug)
89 :
90 : //-----------------------------------------------------------------------------
91 : // nsOfflineCachePendingUpdate
92 : //-----------------------------------------------------------------------------
93 :
94 : class nsOfflineCachePendingUpdate final : public nsIWebProgressListener
95 : , public nsSupportsWeakReference
96 : {
97 : public:
98 : NS_DECL_ISUPPORTS
99 : NS_DECL_NSIWEBPROGRESSLISTENER
100 :
101 0 : nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService,
102 : nsIURI *aManifestURI,
103 : nsIURI *aDocumentURI,
104 : nsIPrincipal* aLoadingPrincipal,
105 : nsIDOMDocument *aDocument)
106 0 : : mService(aService)
107 : , mManifestURI(aManifestURI)
108 : , mDocumentURI(aDocumentURI)
109 : , mLoadingPrincipal(aLoadingPrincipal)
110 0 : , mDidReleaseThis(false)
111 : {
112 0 : mDocument = do_GetWeakReference(aDocument);
113 0 : }
114 :
115 : private:
116 0 : ~nsOfflineCachePendingUpdate() {}
117 :
118 : RefPtr<nsOfflineCacheUpdateService> mService;
119 : nsCOMPtr<nsIURI> mManifestURI;
120 : nsCOMPtr<nsIURI> mDocumentURI;
121 : nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
122 : nsCOMPtr<nsIWeakReference> mDocument;
123 : bool mDidReleaseThis;
124 : };
125 :
126 0 : NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate,
127 : nsIWebProgressListener,
128 : nsISupportsWeakReference)
129 :
130 : //-----------------------------------------------------------------------------
131 : // nsOfflineCacheUpdateService::nsIWebProgressListener
132 : //-----------------------------------------------------------------------------
133 :
134 : NS_IMETHODIMP
135 0 : nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress,
136 : nsIRequest *aRequest,
137 : int32_t curSelfProgress,
138 : int32_t maxSelfProgress,
139 : int32_t curTotalProgress,
140 : int32_t maxTotalProgress)
141 : {
142 0 : NS_NOTREACHED("notification excluded in AddProgressListener(...)");
143 0 : return NS_OK;
144 : }
145 :
146 : NS_IMETHODIMP
147 0 : nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
148 : nsIRequest *aRequest,
149 : uint32_t progressStateFlags,
150 : nsresult aStatus)
151 : {
152 0 : if (mDidReleaseThis) {
153 0 : return NS_OK;
154 : }
155 0 : nsCOMPtr<nsIDOMDocument> updateDoc = do_QueryReferent(mDocument);
156 0 : if (!updateDoc) {
157 : // The document that scheduled this update has gone away,
158 : // we don't need to listen anymore.
159 0 : aWebProgress->RemoveProgressListener(this);
160 0 : MOZ_ASSERT(!mDidReleaseThis);
161 0 : mDidReleaseThis = true;
162 0 : NS_RELEASE_THIS();
163 0 : return NS_OK;
164 : }
165 :
166 0 : if (!(progressStateFlags & STATE_STOP)) {
167 0 : return NS_OK;
168 : }
169 :
170 0 : nsCOMPtr<mozIDOMWindowProxy> windowProxy;
171 0 : aWebProgress->GetDOMWindow(getter_AddRefs(windowProxy));
172 0 : if (!windowProxy) return NS_OK;
173 :
174 0 : auto* outerWindow = nsPIDOMWindowOuter::From(windowProxy);
175 0 : nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow();
176 :
177 0 : nsCOMPtr<nsIDocument> progressDoc = outerWindow->GetDoc();
178 0 : if (!progressDoc) return NS_OK;
179 :
180 0 : if (!SameCOMIdentity(progressDoc, updateDoc)) {
181 0 : return NS_OK;
182 : }
183 :
184 0 : LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
185 : this, progressDoc.get()));
186 :
187 : // Only schedule the update if the document loaded successfully
188 0 : if (NS_SUCCEEDED(aStatus)) {
189 0 : nsCOMPtr<nsIOfflineCacheUpdate> update;
190 0 : mService->Schedule(mManifestURI, mDocumentURI, mLoadingPrincipal, updateDoc, innerWindow,
191 0 : nullptr, getter_AddRefs(update));
192 0 : if (mDidReleaseThis) {
193 0 : return NS_OK;
194 : }
195 : }
196 :
197 0 : aWebProgress->RemoveProgressListener(this);
198 0 : MOZ_ASSERT(!mDidReleaseThis);
199 0 : mDidReleaseThis = true;
200 0 : NS_RELEASE_THIS();
201 :
202 0 : return NS_OK;
203 : }
204 :
205 : NS_IMETHODIMP
206 0 : nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress,
207 : nsIRequest* aRequest,
208 : nsIURI *location,
209 : uint32_t aFlags)
210 : {
211 0 : NS_NOTREACHED("notification excluded in AddProgressListener(...)");
212 0 : return NS_OK;
213 : }
214 :
215 : NS_IMETHODIMP
216 0 : nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
217 : nsIRequest* aRequest,
218 : nsresult aStatus,
219 : const char16_t* aMessage)
220 : {
221 0 : NS_NOTREACHED("notification excluded in AddProgressListener(...)");
222 0 : return NS_OK;
223 : }
224 :
225 : NS_IMETHODIMP
226 0 : nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
227 : nsIRequest *aRequest,
228 : uint32_t state)
229 : {
230 0 : NS_NOTREACHED("notification excluded in AddProgressListener(...)");
231 0 : return NS_OK;
232 : }
233 :
234 : //-----------------------------------------------------------------------------
235 : // nsOfflineCacheUpdateService::nsISupports
236 : //-----------------------------------------------------------------------------
237 :
238 10 : NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService,
239 : nsIOfflineCacheUpdateService,
240 : nsIObserver,
241 : nsISupportsWeakReference)
242 :
243 : //-----------------------------------------------------------------------------
244 : // nsOfflineCacheUpdateService <public>
245 : //-----------------------------------------------------------------------------
246 :
247 1 : nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
248 : : mDisabled(false)
249 : , mUpdateRunning(false)
250 1 : , mLowFreeSpace(false)
251 : {
252 1 : MOZ_ASSERT(NS_IsMainThread());
253 : Preferences::AddBoolVarCache(&sAllowOfflineCache,
254 : "browser.cache.offline.enable",
255 1 : true);
256 1 : }
257 :
258 0 : nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
259 : {
260 0 : gOfflineCacheUpdateService = nullptr;
261 0 : }
262 :
263 : nsresult
264 1 : nsOfflineCacheUpdateService::Init()
265 : {
266 : // Observe xpcom-shutdown event
267 : nsCOMPtr<nsIObserverService> observerService =
268 2 : mozilla::services::GetObserverService();
269 1 : if (!observerService)
270 0 : return NS_ERROR_FAILURE;
271 :
272 2 : nsresult rv = observerService->AddObserver(this,
273 : NS_XPCOM_SHUTDOWN_OBSERVER_ID,
274 2 : true);
275 1 : NS_ENSURE_SUCCESS(rv, rv);
276 :
277 : // Get the current status of the disk in terms of free space and observe
278 : // low device storage notifications.
279 : nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcherService =
280 2 : do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
281 1 : if (diskSpaceWatcherService) {
282 0 : diskSpaceWatcherService->GetIsDiskFull(&mLowFreeSpace);
283 : } else {
284 1 : NS_WARNING("Could not get disk status from nsIDiskSpaceWatcher");
285 : }
286 :
287 1 : rv = observerService->AddObserver(this, "disk-space-watcher", false);
288 1 : NS_ENSURE_SUCCESS(rv, rv);
289 :
290 1 : gOfflineCacheUpdateService = this;
291 :
292 1 : return NS_OK;
293 : }
294 :
295 : /* static */
296 : nsOfflineCacheUpdateService *
297 1 : nsOfflineCacheUpdateService::GetInstance()
298 : {
299 1 : if (!gOfflineCacheUpdateService) {
300 1 : gOfflineCacheUpdateService = new nsOfflineCacheUpdateService();
301 1 : if (!gOfflineCacheUpdateService)
302 0 : return nullptr;
303 1 : NS_ADDREF(gOfflineCacheUpdateService);
304 1 : nsresult rv = gOfflineCacheUpdateService->Init();
305 1 : if (NS_FAILED(rv)) {
306 0 : NS_RELEASE(gOfflineCacheUpdateService);
307 0 : return nullptr;
308 : }
309 1 : return gOfflineCacheUpdateService;
310 : }
311 :
312 0 : NS_ADDREF(gOfflineCacheUpdateService);
313 :
314 0 : return gOfflineCacheUpdateService;
315 : }
316 :
317 : /* static */
318 : nsOfflineCacheUpdateService *
319 0 : nsOfflineCacheUpdateService::EnsureService()
320 : {
321 0 : if (!gOfflineCacheUpdateService) {
322 : // Make the service manager hold a long-lived reference to the service
323 : nsCOMPtr<nsIOfflineCacheUpdateService> service =
324 0 : do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
325 : }
326 :
327 0 : return gOfflineCacheUpdateService;
328 : }
329 :
330 : nsresult
331 0 : nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate)
332 : {
333 0 : LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
334 : this, aUpdate));
335 :
336 0 : aUpdate->SetOwner(this);
337 :
338 0 : mUpdates.AppendElement(aUpdate);
339 0 : ProcessNextUpdate();
340 :
341 0 : return NS_OK;
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
346 : nsIURI *aDocumentURI,
347 : nsIPrincipal* aLoadingPrincipal,
348 : nsIDOMDocument *aDocument)
349 : {
350 0 : LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
351 : this, aManifestURI, aDocumentURI, aDocument));
352 :
353 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
354 0 : nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(doc->GetContainer());
355 0 : NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG);
356 :
357 : // Proceed with cache update
358 : RefPtr<nsOfflineCachePendingUpdate> update =
359 : new nsOfflineCachePendingUpdate(this, aManifestURI, aDocumentURI,
360 0 : aLoadingPrincipal, aDocument);
361 0 : NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
362 :
363 0 : nsresult rv = progress->AddProgressListener
364 0 : (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
365 0 : NS_ENSURE_SUCCESS(rv, rv);
366 :
367 : // The update will release when it has scheduled itself.
368 0 : Unused << update.forget();
369 :
370 0 : return NS_OK;
371 : }
372 :
373 : nsresult
374 0 : nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
375 : {
376 0 : LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]",
377 : this, aUpdate));
378 :
379 0 : NS_ASSERTION(mUpdates.Length() > 0 &&
380 : mUpdates[0] == aUpdate, "Unknown update completed");
381 :
382 : // keep this item alive until we're done notifying observers
383 0 : RefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
384 : Unused << update;
385 0 : mUpdates.RemoveElementAt(0);
386 0 : mUpdateRunning = false;
387 :
388 0 : ProcessNextUpdate();
389 :
390 0 : return NS_OK;
391 : }
392 :
393 : //-----------------------------------------------------------------------------
394 : // nsOfflineCacheUpdateService <private>
395 : //-----------------------------------------------------------------------------
396 :
397 : nsresult
398 0 : nsOfflineCacheUpdateService::ProcessNextUpdate()
399 : {
400 0 : LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%" PRIuSIZE "]",
401 : this, mUpdates.Length()));
402 :
403 0 : if (mDisabled)
404 0 : return NS_ERROR_ABORT;
405 :
406 0 : if (mUpdateRunning)
407 0 : return NS_OK;
408 :
409 0 : if (mUpdates.Length() > 0) {
410 0 : mUpdateRunning = true;
411 : // Canceling the update before Begin() call will make the update
412 : // asynchronously finish with an error.
413 0 : if (mLowFreeSpace) {
414 0 : mUpdates[0]->Cancel();
415 : }
416 0 : return mUpdates[0]->Begin();
417 : }
418 :
419 0 : return NS_OK;
420 : }
421 :
422 : //-----------------------------------------------------------------------------
423 : // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
424 : //-----------------------------------------------------------------------------
425 :
426 : NS_IMETHODIMP
427 0 : nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates)
428 : {
429 0 : LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
430 :
431 0 : *aNumUpdates = mUpdates.Length();
432 0 : return NS_OK;
433 : }
434 :
435 : NS_IMETHODIMP
436 0 : nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex,
437 : nsIOfflineCacheUpdate **aUpdate)
438 : {
439 0 : LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
440 :
441 0 : if (aIndex < mUpdates.Length()) {
442 0 : NS_ADDREF(*aUpdate = mUpdates[aIndex]);
443 : } else {
444 0 : *aUpdate = nullptr;
445 : }
446 :
447 0 : return NS_OK;
448 : }
449 :
450 : nsresult
451 0 : nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
452 : nsACString const &aOriginSuffix,
453 : nsIFile *aCustomProfileDir,
454 : nsOfflineCacheUpdate **aUpdate)
455 : {
456 : nsresult rv;
457 :
458 : nsCOMPtr<nsIApplicationCacheService> cacheService =
459 0 : do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
460 0 : NS_ENSURE_SUCCESS(rv, rv);
461 :
462 0 : nsAutoCString groupID;
463 0 : rv = cacheService->BuildGroupIDForSuffix(aManifestURI, aOriginSuffix, groupID);
464 0 : NS_ENSURE_SUCCESS(rv, rv);
465 :
466 0 : RefPtr<nsOfflineCacheUpdate> update;
467 0 : for (uint32_t i = 0; i < mUpdates.Length(); i++) {
468 0 : update = mUpdates[i];
469 :
470 : bool partial;
471 0 : rv = update->GetPartial(&partial);
472 0 : NS_ENSURE_SUCCESS(rv, rv);
473 :
474 0 : if (partial) {
475 : // Partial updates aren't considered
476 0 : continue;
477 : }
478 :
479 0 : if (update->IsForGroupID(groupID) && update->IsForProfile(aCustomProfileDir)) {
480 0 : update.swap(*aUpdate);
481 0 : return NS_OK;
482 : }
483 : }
484 :
485 0 : return NS_ERROR_NOT_AVAILABLE;
486 : }
487 :
488 : nsresult
489 0 : nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
490 : nsIURI *aDocumentURI,
491 : nsIPrincipal* aLoadingPrincipal,
492 : nsIDOMDocument *aDocument,
493 : nsPIDOMWindowInner* aWindow,
494 : nsIFile* aCustomProfileDir,
495 : nsIOfflineCacheUpdate **aUpdate)
496 : {
497 0 : nsCOMPtr<nsIOfflineCacheUpdate> update;
498 0 : if (GeckoProcessType_Default != XRE_GetProcessType()) {
499 0 : update = new OfflineCacheUpdateChild(aWindow);
500 : }
501 : else {
502 0 : update = new OfflineCacheUpdateGlue();
503 : }
504 :
505 : nsresult rv;
506 :
507 0 : if (aWindow) {
508 : // Ensure there is window.applicationCache object that is
509 : // responsible for association of the new applicationCache
510 : // with the corresponding document. Just ignore the result.
511 : nsCOMPtr<nsIDOMOfflineResourceList> appCacheWindowObject =
512 0 : aWindow->GetApplicationCache();
513 : }
514 :
515 0 : rv = update->Init(aManifestURI, aDocumentURI, aLoadingPrincipal, aDocument,
516 0 : aCustomProfileDir);
517 0 : NS_ENSURE_SUCCESS(rv, rv);
518 :
519 0 : rv = update->Schedule();
520 0 : NS_ENSURE_SUCCESS(rv, rv);
521 :
522 0 : NS_ADDREF(*aUpdate = update);
523 :
524 0 : return NS_OK;
525 : }
526 :
527 : NS_IMETHODIMP
528 0 : nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
529 : nsIURI *aDocumentURI,
530 : nsIPrincipal* aLoadingPrincipal,
531 : mozIDOMWindow* aWindow,
532 : nsIOfflineCacheUpdate **aUpdate)
533 : {
534 0 : return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr,
535 0 : nsPIDOMWindowInner::From(aWindow), nullptr, aUpdate);
536 : }
537 :
538 : NS_IMETHODIMP
539 0 : nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI,
540 : nsIURI *aDocumentURI,
541 : nsIPrincipal* aLoadingPrincipal,
542 : nsIFile *aProfileDir,
543 : nsIOfflineCacheUpdate **aUpdate)
544 : {
545 : return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr, nullptr,
546 0 : aProfileDir, aUpdate);
547 : }
548 :
549 0 : NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI,
550 : nsIPrincipal* aLoadingPrincipal,
551 : nsIObserver *aObserver)
552 : {
553 0 : if (GeckoProcessType_Default != XRE_GetProcessType()) {
554 : // Not intended to support this on child processes
555 0 : return NS_ERROR_NOT_IMPLEMENTED;
556 : }
557 :
558 0 : nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue();
559 :
560 : nsresult rv;
561 :
562 0 : rv = update->InitForUpdateCheck(aManifestURI, aLoadingPrincipal, aObserver);
563 0 : NS_ENSURE_SUCCESS(rv, rv);
564 :
565 0 : rv = update->Schedule();
566 0 : NS_ENSURE_SUCCESS(rv, rv);
567 :
568 0 : return NS_OK;
569 : }
570 :
571 : //-----------------------------------------------------------------------------
572 : // nsOfflineCacheUpdateService::nsIObserver
573 : //-----------------------------------------------------------------------------
574 :
575 : NS_IMETHODIMP
576 0 : nsOfflineCacheUpdateService::Observe(nsISupports *aSubject,
577 : const char *aTopic,
578 : const char16_t *aData)
579 : {
580 0 : if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
581 0 : if (mUpdates.Length() > 0)
582 0 : mUpdates[0]->Cancel();
583 0 : mDisabled = true;
584 : }
585 :
586 0 : if (!strcmp(aTopic, "disk-space-watcher")) {
587 0 : if (NS_LITERAL_STRING("full").Equals(aData)) {
588 0 : mLowFreeSpace = true;
589 0 : for (uint32_t i = 0; i < mUpdates.Length(); i++) {
590 0 : mUpdates[i]->Cancel();
591 : }
592 0 : } else if (NS_LITERAL_STRING("free").Equals(aData)) {
593 0 : mLowFreeSpace = false;
594 : }
595 : }
596 :
597 0 : return NS_OK;
598 : }
599 :
600 : //-----------------------------------------------------------------------------
601 : // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
602 : //-----------------------------------------------------------------------------
603 :
604 : static nsresult
605 1 : OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal,
606 : nsIPrefBranch *aPrefBranch,
607 : bool pinned,
608 : bool *aAllowed)
609 : {
610 1 : *aAllowed = false;
611 :
612 1 : if (!sAllowOfflineCache) {
613 0 : return NS_OK;
614 : }
615 :
616 1 : if (!aPrincipal)
617 0 : return NS_ERROR_INVALID_ARG;
618 :
619 2 : nsCOMPtr<nsIURI> uri;
620 1 : aPrincipal->GetURI(getter_AddRefs(uri));
621 :
622 1 : if (!uri)
623 0 : return NS_OK;
624 :
625 2 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
626 1 : if (!innerURI)
627 0 : return NS_OK;
628 :
629 : // only http and https applications can use offline APIs.
630 : bool match;
631 1 : nsresult rv = innerURI->SchemeIs("http", &match);
632 1 : NS_ENSURE_SUCCESS(rv, rv);
633 :
634 1 : if (!match) {
635 0 : rv = innerURI->SchemeIs("https", &match);
636 0 : NS_ENSURE_SUCCESS(rv, rv);
637 0 : if (!match) {
638 0 : return NS_OK;
639 : }
640 : }
641 :
642 2 : nsAutoCString domain;
643 1 : rv = innerURI->GetAsciiHost(domain);
644 1 : NS_ENSURE_SUCCESS(rv, rv);
645 :
646 1 : if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) {
647 0 : *aAllowed = true;
648 0 : return NS_OK;
649 : }
650 :
651 : nsCOMPtr<nsIPermissionManager> permissionManager =
652 2 : services::GetPermissionManager();
653 1 : if (!permissionManager) {
654 0 : return NS_OK;
655 : }
656 :
657 : uint32_t perm;
658 1 : const char *permName = pinned ? "pin-app" : "offline-app";
659 1 : permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm);
660 :
661 2 : if (perm == nsIPermissionManager::ALLOW_ACTION ||
662 1 : perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
663 0 : *aAllowed = true;
664 : }
665 :
666 : // offline-apps.allow_by_default is now effective at the cache selection
667 : // algorithm code (nsContentSink).
668 :
669 1 : return NS_OK;
670 : }
671 :
672 : NS_IMETHODIMP
673 1 : nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal,
674 : nsIPrefBranch *aPrefBranch,
675 : bool *aAllowed)
676 : {
677 1 : return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed);
678 : }
679 :
680 : NS_IMETHODIMP
681 0 : nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,
682 : nsIPrefBranch *aPrefBranch,
683 : bool *aAllowed)
684 : {
685 0 : OriginAttributes attrs;
686 : nsCOMPtr<nsIPrincipal> principal =
687 0 : BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
688 0 : return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed);
689 : }
690 :
691 : nsresult
692 0 : nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI,
693 : nsIPrefBranch *aPrefBranch,
694 : bool *aPinned)
695 : {
696 0 : OriginAttributes attrs;
697 : nsCOMPtr<nsIPrincipal> principal =
698 0 : BasePrincipal::CreateCodebasePrincipal(aDocumentURI, attrs);
699 0 : return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned);
700 : }
701 :
702 : NS_IMETHODIMP
703 0 : nsOfflineCacheUpdateService::AllowOfflineApp(nsIPrincipal *aPrincipal)
704 : {
705 : nsresult rv;
706 :
707 0 : if (!sAllowOfflineCache) {
708 0 : return NS_ERROR_NOT_AVAILABLE;
709 : }
710 :
711 0 : if (GeckoProcessType_Default != XRE_GetProcessType()) {
712 0 : ContentChild* child = ContentChild::GetSingleton();
713 :
714 0 : if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) {
715 0 : return NS_ERROR_FAILURE;
716 : }
717 :
718 0 : nsAutoCString domain;
719 0 : rv = aPrincipal->GetBaseDomain(domain);
720 0 : NS_ENSURE_SUCCESS(rv, rv);
721 :
722 0 : nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain);
723 : }
724 : else {
725 : nsCOMPtr<nsIPermissionManager> permissionManager =
726 0 : services::GetPermissionManager();
727 0 : if (!permissionManager)
728 0 : return NS_ERROR_NOT_AVAILABLE;
729 :
730 0 : rv = permissionManager->AddFromPrincipal(
731 : aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION,
732 0 : nsIPermissionManager::EXPIRE_NEVER, 0);
733 0 : NS_ENSURE_SUCCESS(rv, rv);
734 : }
735 :
736 0 : return NS_OK;
737 : }
|