uriloader/prefetch/nsOfflineCacheUpdateService.cpp

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

mercurial