1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,781 @@ 1.4 +/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#if defined(MOZ_LOGGING) 1.10 +#define FORCE_PR_LOG 1.11 +#endif 1.12 + 1.13 +#include "OfflineCacheUpdateChild.h" 1.14 +#include "OfflineCacheUpdateParent.h" 1.15 +#include "nsXULAppAPI.h" 1.16 +#include "OfflineCacheUpdateGlue.h" 1.17 +#include "nsOfflineCacheUpdate.h" 1.18 + 1.19 +#include "nsCPrefetchService.h" 1.20 +#include "nsCURILoader.h" 1.21 +#include "nsIApplicationCacheContainer.h" 1.22 +#include "nsIApplicationCacheChannel.h" 1.23 +#include "nsIApplicationCacheService.h" 1.24 +#include "nsICache.h" 1.25 +#include "nsICacheService.h" 1.26 +#include "nsICacheSession.h" 1.27 +#include "nsICachingChannel.h" 1.28 +#include "nsIContent.h" 1.29 +#include "nsIDocShell.h" 1.30 +#include "nsIDocumentLoader.h" 1.31 +#include "nsIDOMElement.h" 1.32 +#include "nsIDOMWindow.h" 1.33 +#include "nsIDOMOfflineResourceList.h" 1.34 +#include "nsIDocument.h" 1.35 +#include "nsIObserverService.h" 1.36 +#include "nsIURL.h" 1.37 +#include "nsIWebProgress.h" 1.38 +#include "nsIWebNavigation.h" 1.39 +#include "nsICryptoHash.h" 1.40 +#include "nsICacheEntryDescriptor.h" 1.41 +#include "nsIPermissionManager.h" 1.42 +#include "nsIPrincipal.h" 1.43 +#include "nsIScriptSecurityManager.h" 1.44 +#include "nsNetCID.h" 1.45 +#include "nsNetUtil.h" 1.46 +#include "nsServiceManagerUtils.h" 1.47 +#include "nsStreamUtils.h" 1.48 +#include "nsThreadUtils.h" 1.49 +#include "nsProxyRelease.h" 1.50 +#include "prlog.h" 1.51 +#include "nsIAsyncVerifyRedirectCallback.h" 1.52 +#include "mozilla/Preferences.h" 1.53 +#include "mozilla/Attributes.h" 1.54 +#include "mozilla/unused.h" 1.55 +#include "nsIDiskSpaceWatcher.h" 1.56 +#include "nsIDocShell.h" 1.57 +#include "nsIDocShellTreeItem.h" 1.58 +#include "nsIDocShellTreeOwner.h" 1.59 +#include "mozilla/dom/TabChild.h" 1.60 +#include "mozilla/dom/PermissionMessageUtils.h" 1.61 +#include "nsContentUtils.h" 1.62 +#include "mozilla/unused.h" 1.63 + 1.64 +using namespace mozilla; 1.65 +using namespace mozilla::dom; 1.66 + 1.67 +static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr; 1.68 + 1.69 +nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr; 1.70 + 1.71 +nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains() 1.72 +{ 1.73 + if (!mAllowedDomains) 1.74 + mAllowedDomains = new nsTHashtable<nsCStringHashKey>(); 1.75 + 1.76 + return mAllowedDomains; 1.77 +} 1.78 + 1.79 + 1.80 +typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent; 1.81 +typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild; 1.82 +typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue; 1.83 + 1.84 +#if defined(PR_LOGGING) 1.85 +// 1.86 +// To enable logging (see prlog.h for full details): 1.87 +// 1.88 +// set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 1.89 +// set NSPR_LOG_FILE=offlineupdate.log 1.90 +// 1.91 +// this enables PR_LOG_ALWAYS level information and places all output in 1.92 +// the file offlineupdate.log 1.93 +// 1.94 +PRLogModuleInfo *gOfflineCacheUpdateLog; 1.95 +#endif 1.96 + 1.97 +#undef LOG 1.98 +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) 1.99 + 1.100 +#undef LOG_ENABLED 1.101 +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) 1.102 + 1.103 +namespace { // anon 1.104 + 1.105 +nsresult 1.106 +GetAppIDAndInBrowserFromWindow(nsIDOMWindow *aWindow, 1.107 + uint32_t *aAppId, 1.108 + bool *aInBrowser) 1.109 +{ 1.110 + *aAppId = NECKO_NO_APP_ID; 1.111 + *aInBrowser = false; 1.112 + 1.113 + if (!aWindow) { 1.114 + return NS_OK; 1.115 + } 1.116 + 1.117 + nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aWindow); 1.118 + if (!loadContext) { 1.119 + return NS_OK; 1.120 + } 1.121 + 1.122 + nsresult rv; 1.123 + 1.124 + rv = loadContext->GetAppId(aAppId); 1.125 + NS_ENSURE_SUCCESS(rv, rv); 1.126 + 1.127 + rv = loadContext->GetIsInBrowserElement(aInBrowser); 1.128 + NS_ENSURE_SUCCESS(rv, rv); 1.129 + 1.130 + return NS_OK; 1.131 +} 1.132 + 1.133 +} // anon 1.134 + 1.135 +//----------------------------------------------------------------------------- 1.136 +// nsOfflineCachePendingUpdate 1.137 +//----------------------------------------------------------------------------- 1.138 + 1.139 +class nsOfflineCachePendingUpdate MOZ_FINAL : public nsIWebProgressListener 1.140 + , public nsSupportsWeakReference 1.141 +{ 1.142 +public: 1.143 + NS_DECL_ISUPPORTS 1.144 + NS_DECL_NSIWEBPROGRESSLISTENER 1.145 + 1.146 + nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService, 1.147 + nsIURI *aManifestURI, 1.148 + nsIURI *aDocumentURI, 1.149 + nsIDOMDocument *aDocument) 1.150 + : mService(aService) 1.151 + , mManifestURI(aManifestURI) 1.152 + , mDocumentURI(aDocumentURI) 1.153 + , mDidReleaseThis(false) 1.154 + { 1.155 + mDocument = do_GetWeakReference(aDocument); 1.156 + } 1.157 + 1.158 +private: 1.159 + nsRefPtr<nsOfflineCacheUpdateService> mService; 1.160 + nsCOMPtr<nsIURI> mManifestURI; 1.161 + nsCOMPtr<nsIURI> mDocumentURI; 1.162 + nsCOMPtr<nsIWeakReference> mDocument; 1.163 + bool mDidReleaseThis; 1.164 +}; 1.165 + 1.166 +NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate, 1.167 + nsIWebProgressListener, 1.168 + nsISupportsWeakReference) 1.169 + 1.170 +//----------------------------------------------------------------------------- 1.171 +// nsOfflineCacheUpdateService::nsIWebProgressListener 1.172 +//----------------------------------------------------------------------------- 1.173 + 1.174 +NS_IMETHODIMP 1.175 +nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress, 1.176 + nsIRequest *aRequest, 1.177 + int32_t curSelfProgress, 1.178 + int32_t maxSelfProgress, 1.179 + int32_t curTotalProgress, 1.180 + int32_t maxTotalProgress) 1.181 +{ 1.182 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.183 + return NS_OK; 1.184 +} 1.185 + 1.186 +NS_IMETHODIMP 1.187 +nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress, 1.188 + nsIRequest *aRequest, 1.189 + uint32_t progressStateFlags, 1.190 + nsresult aStatus) 1.191 +{ 1.192 + if (mDidReleaseThis) { 1.193 + return NS_OK; 1.194 + } 1.195 + nsCOMPtr<nsIDOMDocument> updateDoc = do_QueryReferent(mDocument); 1.196 + if (!updateDoc) { 1.197 + // The document that scheduled this update has gone away, 1.198 + // we don't need to listen anymore. 1.199 + aWebProgress->RemoveProgressListener(this); 1.200 + MOZ_ASSERT(!mDidReleaseThis); 1.201 + mDidReleaseThis = true; 1.202 + NS_RELEASE_THIS(); 1.203 + return NS_OK; 1.204 + } 1.205 + 1.206 + if (!(progressStateFlags & STATE_STOP)) { 1.207 + return NS_OK; 1.208 + } 1.209 + 1.210 + nsCOMPtr<nsIDOMWindow> window; 1.211 + aWebProgress->GetDOMWindow(getter_AddRefs(window)); 1.212 + if (!window) return NS_OK; 1.213 + 1.214 + nsCOMPtr<nsIDOMDocument> progressDoc; 1.215 + window->GetDocument(getter_AddRefs(progressDoc)); 1.216 + if (!progressDoc) return NS_OK; 1.217 + 1.218 + if (!SameCOMIdentity(progressDoc, updateDoc)) { 1.219 + return NS_OK; 1.220 + } 1.221 + 1.222 + LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]", 1.223 + this, progressDoc.get())); 1.224 + 1.225 + // Only schedule the update if the document loaded successfully 1.226 + if (NS_SUCCEEDED(aStatus)) { 1.227 + // Get extended origin attributes 1.228 + uint32_t appId; 1.229 + bool isInBrowserElement; 1.230 + nsresult rv = GetAppIDAndInBrowserFromWindow(window, &appId, &isInBrowserElement); 1.231 + NS_ENSURE_SUCCESS(rv, rv); 1.232 + 1.233 + nsCOMPtr<nsIOfflineCacheUpdate> update; 1.234 + mService->Schedule(mManifestURI, mDocumentURI, 1.235 + updateDoc, window, nullptr, 1.236 + appId, isInBrowserElement, getter_AddRefs(update)); 1.237 + if (mDidReleaseThis) { 1.238 + return NS_OK; 1.239 + } 1.240 + } 1.241 + 1.242 + aWebProgress->RemoveProgressListener(this); 1.243 + MOZ_ASSERT(!mDidReleaseThis); 1.244 + mDidReleaseThis = true; 1.245 + NS_RELEASE_THIS(); 1.246 + 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress, 1.252 + nsIRequest* aRequest, 1.253 + nsIURI *location, 1.254 + uint32_t aFlags) 1.255 +{ 1.256 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.257 + return NS_OK; 1.258 +} 1.259 + 1.260 +NS_IMETHODIMP 1.261 +nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress, 1.262 + nsIRequest* aRequest, 1.263 + nsresult aStatus, 1.264 + const char16_t* aMessage) 1.265 +{ 1.266 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.267 + return NS_OK; 1.268 +} 1.269 + 1.270 +NS_IMETHODIMP 1.271 +nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress, 1.272 + nsIRequest *aRequest, 1.273 + uint32_t state) 1.274 +{ 1.275 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.276 + return NS_OK; 1.277 +} 1.278 + 1.279 +//----------------------------------------------------------------------------- 1.280 +// nsOfflineCacheUpdateService::nsISupports 1.281 +//----------------------------------------------------------------------------- 1.282 + 1.283 +NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService, 1.284 + nsIOfflineCacheUpdateService, 1.285 + nsIObserver, 1.286 + nsISupportsWeakReference) 1.287 + 1.288 +//----------------------------------------------------------------------------- 1.289 +// nsOfflineCacheUpdateService <public> 1.290 +//----------------------------------------------------------------------------- 1.291 + 1.292 +nsOfflineCacheUpdateService::nsOfflineCacheUpdateService() 1.293 + : mDisabled(false) 1.294 + , mUpdateRunning(false) 1.295 + , mLowFreeSpace(false) 1.296 +{ 1.297 +} 1.298 + 1.299 +nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() 1.300 +{ 1.301 + gOfflineCacheUpdateService = nullptr; 1.302 +} 1.303 + 1.304 +nsresult 1.305 +nsOfflineCacheUpdateService::Init() 1.306 +{ 1.307 +#if defined(PR_LOGGING) 1.308 + if (!gOfflineCacheUpdateLog) 1.309 + gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate"); 1.310 +#endif 1.311 + 1.312 + // Observe xpcom-shutdown event 1.313 + nsCOMPtr<nsIObserverService> observerService = 1.314 + mozilla::services::GetObserverService(); 1.315 + if (!observerService) 1.316 + return NS_ERROR_FAILURE; 1.317 + 1.318 + nsresult rv = observerService->AddObserver(this, 1.319 + NS_XPCOM_SHUTDOWN_OBSERVER_ID, 1.320 + true); 1.321 + NS_ENSURE_SUCCESS(rv, rv); 1.322 + 1.323 + // Get the current status of the disk in terms of free space and observe 1.324 + // low device storage notifications. 1.325 + nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcherService = 1.326 + do_GetService("@mozilla.org/toolkit/disk-space-watcher;1"); 1.327 + if (diskSpaceWatcherService) { 1.328 + diskSpaceWatcherService->GetIsDiskFull(&mLowFreeSpace); 1.329 + } else { 1.330 + NS_WARNING("Could not get disk status from nsIDiskSpaceWatcher"); 1.331 + } 1.332 + 1.333 + rv = observerService->AddObserver(this, "disk-space-watcher", false); 1.334 + NS_ENSURE_SUCCESS(rv, rv); 1.335 + 1.336 + gOfflineCacheUpdateService = this; 1.337 + 1.338 + return NS_OK; 1.339 +} 1.340 + 1.341 +/* static */ 1.342 +nsOfflineCacheUpdateService * 1.343 +nsOfflineCacheUpdateService::GetInstance() 1.344 +{ 1.345 + if (!gOfflineCacheUpdateService) { 1.346 + gOfflineCacheUpdateService = new nsOfflineCacheUpdateService(); 1.347 + if (!gOfflineCacheUpdateService) 1.348 + return nullptr; 1.349 + NS_ADDREF(gOfflineCacheUpdateService); 1.350 + nsresult rv = gOfflineCacheUpdateService->Init(); 1.351 + if (NS_FAILED(rv)) { 1.352 + NS_RELEASE(gOfflineCacheUpdateService); 1.353 + return nullptr; 1.354 + } 1.355 + return gOfflineCacheUpdateService; 1.356 + } 1.357 + 1.358 + NS_ADDREF(gOfflineCacheUpdateService); 1.359 + 1.360 + return gOfflineCacheUpdateService; 1.361 +} 1.362 + 1.363 +/* static */ 1.364 +nsOfflineCacheUpdateService * 1.365 +nsOfflineCacheUpdateService::EnsureService() 1.366 +{ 1.367 + if (!gOfflineCacheUpdateService) { 1.368 + // Make the service manager hold a long-lived reference to the service 1.369 + nsCOMPtr<nsIOfflineCacheUpdateService> service = 1.370 + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); 1.371 + } 1.372 + 1.373 + return gOfflineCacheUpdateService; 1.374 +} 1.375 + 1.376 +nsresult 1.377 +nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate) 1.378 +{ 1.379 + LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]", 1.380 + this, aUpdate)); 1.381 + 1.382 + aUpdate->SetOwner(this); 1.383 + 1.384 + mUpdates.AppendElement(aUpdate); 1.385 + ProcessNextUpdate(); 1.386 + 1.387 + return NS_OK; 1.388 +} 1.389 + 1.390 +NS_IMETHODIMP 1.391 +nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI, 1.392 + nsIURI *aDocumentURI, 1.393 + nsIDOMDocument *aDocument) 1.394 +{ 1.395 + LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]", 1.396 + this, aManifestURI, aDocumentURI, aDocument)); 1.397 + 1.398 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument); 1.399 + nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(doc->GetContainer()); 1.400 + NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG); 1.401 + 1.402 + // Proceed with cache update 1.403 + nsRefPtr<nsOfflineCachePendingUpdate> update = 1.404 + new nsOfflineCachePendingUpdate(this, aManifestURI, 1.405 + aDocumentURI, aDocument); 1.406 + NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); 1.407 + 1.408 + nsresult rv = progress->AddProgressListener 1.409 + (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT); 1.410 + NS_ENSURE_SUCCESS(rv, rv); 1.411 + 1.412 + // The update will release when it has scheduled itself. 1.413 + unused << update.forget(); 1.414 + 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 +nsresult 1.419 +nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate) 1.420 +{ 1.421 + LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]", 1.422 + this, aUpdate)); 1.423 + 1.424 + NS_ASSERTION(mUpdates.Length() > 0 && 1.425 + mUpdates[0] == aUpdate, "Unknown update completed"); 1.426 + 1.427 + // keep this item alive until we're done notifying observers 1.428 + nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0]; 1.429 + mUpdates.RemoveElementAt(0); 1.430 + mUpdateRunning = false; 1.431 + 1.432 + ProcessNextUpdate(); 1.433 + 1.434 + return NS_OK; 1.435 +} 1.436 + 1.437 +//----------------------------------------------------------------------------- 1.438 +// nsOfflineCacheUpdateService <private> 1.439 +//----------------------------------------------------------------------------- 1.440 + 1.441 +nsresult 1.442 +nsOfflineCacheUpdateService::ProcessNextUpdate() 1.443 +{ 1.444 + LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]", 1.445 + this, mUpdates.Length())); 1.446 + 1.447 + if (mDisabled) 1.448 + return NS_ERROR_ABORT; 1.449 + 1.450 + if (mUpdateRunning) 1.451 + return NS_OK; 1.452 + 1.453 + if (mUpdates.Length() > 0) { 1.454 + mUpdateRunning = true; 1.455 + // Canceling the update before Begin() call will make the update 1.456 + // asynchronously finish with an error. 1.457 + if (mLowFreeSpace) { 1.458 + mUpdates[0]->Cancel(); 1.459 + } 1.460 + return mUpdates[0]->Begin(); 1.461 + } 1.462 + 1.463 + return NS_OK; 1.464 +} 1.465 + 1.466 +//----------------------------------------------------------------------------- 1.467 +// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService 1.468 +//----------------------------------------------------------------------------- 1.469 + 1.470 +NS_IMETHODIMP 1.471 +nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates) 1.472 +{ 1.473 + LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this)); 1.474 + 1.475 + *aNumUpdates = mUpdates.Length(); 1.476 + return NS_OK; 1.477 +} 1.478 + 1.479 +NS_IMETHODIMP 1.480 +nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex, 1.481 + nsIOfflineCacheUpdate **aUpdate) 1.482 +{ 1.483 + LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex)); 1.484 + 1.485 + if (aIndex < mUpdates.Length()) { 1.486 + NS_ADDREF(*aUpdate = mUpdates[aIndex]); 1.487 + } else { 1.488 + *aUpdate = nullptr; 1.489 + } 1.490 + 1.491 + return NS_OK; 1.492 +} 1.493 + 1.494 +nsresult 1.495 +nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI, 1.496 + uint32_t aAppID, 1.497 + bool aInBrowser, 1.498 + nsOfflineCacheUpdate **aUpdate) 1.499 +{ 1.500 + nsresult rv; 1.501 + 1.502 + nsCOMPtr<nsIApplicationCacheService> cacheService = 1.503 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.504 + NS_ENSURE_SUCCESS(rv, rv); 1.505 + 1.506 + nsAutoCString groupID; 1.507 + rv = cacheService->BuildGroupIDForApp(aManifestURI, 1.508 + aAppID, aInBrowser, 1.509 + groupID); 1.510 + NS_ENSURE_SUCCESS(rv, rv); 1.511 + 1.512 + nsRefPtr<nsOfflineCacheUpdate> update; 1.513 + for (uint32_t i = 0; i < mUpdates.Length(); i++) { 1.514 + update = mUpdates[i]; 1.515 + 1.516 + bool partial; 1.517 + rv = update->GetPartial(&partial); 1.518 + NS_ENSURE_SUCCESS(rv, rv); 1.519 + 1.520 + if (partial) { 1.521 + // Partial updates aren't considered 1.522 + continue; 1.523 + } 1.524 + 1.525 + if (update->IsForGroupID(groupID)) { 1.526 + update.swap(*aUpdate); 1.527 + return NS_OK; 1.528 + } 1.529 + } 1.530 + 1.531 + return NS_ERROR_NOT_AVAILABLE; 1.532 +} 1.533 + 1.534 +nsresult 1.535 +nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI, 1.536 + nsIURI *aDocumentURI, 1.537 + nsIDOMDocument *aDocument, 1.538 + nsIDOMWindow* aWindow, 1.539 + nsIFile* aCustomProfileDir, 1.540 + uint32_t aAppID, 1.541 + bool aInBrowser, 1.542 + nsIOfflineCacheUpdate **aUpdate) 1.543 +{ 1.544 + nsCOMPtr<nsIOfflineCacheUpdate> update; 1.545 + if (GeckoProcessType_Default != XRE_GetProcessType()) { 1.546 + update = new OfflineCacheUpdateChild(aWindow); 1.547 + } 1.548 + else { 1.549 + update = new OfflineCacheUpdateGlue(); 1.550 + } 1.551 + 1.552 + nsresult rv; 1.553 + 1.554 + if (aWindow) { 1.555 + // Ensure there is window.applicationCache object that is 1.556 + // responsible for association of the new applicationCache 1.557 + // with the corresponding document. Just ignore the result. 1.558 + nsCOMPtr<nsIDOMOfflineResourceList> appCacheWindowObject; 1.559 + aWindow->GetApplicationCache(getter_AddRefs(appCacheWindowObject)); 1.560 + } 1.561 + 1.562 + rv = update->Init(aManifestURI, aDocumentURI, aDocument, 1.563 + aCustomProfileDir, aAppID, aInBrowser); 1.564 + NS_ENSURE_SUCCESS(rv, rv); 1.565 + 1.566 + rv = update->Schedule(); 1.567 + NS_ENSURE_SUCCESS(rv, rv); 1.568 + 1.569 + NS_ADDREF(*aUpdate = update); 1.570 + 1.571 + return NS_OK; 1.572 +} 1.573 + 1.574 +NS_IMETHODIMP 1.575 +nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI, 1.576 + nsIURI *aDocumentURI, 1.577 + nsIDOMWindow *aWindow, 1.578 + nsIOfflineCacheUpdate **aUpdate) 1.579 +{ 1.580 + // Get extended origin attributes 1.581 + uint32_t appId; 1.582 + bool isInBrowser; 1.583 + nsresult rv = GetAppIDAndInBrowserFromWindow(aWindow, &appId, &isInBrowser); 1.584 + NS_ENSURE_SUCCESS(rv, rv); 1.585 + 1.586 + return Schedule(aManifestURI, aDocumentURI, nullptr, aWindow, nullptr, 1.587 + appId, isInBrowser, aUpdate); 1.588 +} 1.589 + 1.590 +NS_IMETHODIMP 1.591 +nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI, 1.592 + nsIURI *aDocumentURI, 1.593 + uint32_t aAppID, bool aInBrowser, 1.594 + nsIFile *aProfileDir, 1.595 + nsIOfflineCacheUpdate **aUpdate) 1.596 +{ 1.597 + return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, aProfileDir, 1.598 + aAppID, aInBrowser, aUpdate); 1.599 +} 1.600 + 1.601 +NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI, 1.602 + uint32_t aAppID, 1.603 + bool aInBrowser, 1.604 + nsIObserver *aObserver) 1.605 +{ 1.606 + if (GeckoProcessType_Default != XRE_GetProcessType()) { 1.607 + // Not intended to support this on child processes 1.608 + return NS_ERROR_NOT_IMPLEMENTED; 1.609 + } 1.610 + 1.611 + nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue(); 1.612 + 1.613 + nsresult rv; 1.614 + 1.615 + rv = update->InitForUpdateCheck(aManifestURI, aAppID, aInBrowser, aObserver); 1.616 + NS_ENSURE_SUCCESS(rv, rv); 1.617 + 1.618 + rv = update->Schedule(); 1.619 + NS_ENSURE_SUCCESS(rv, rv); 1.620 + 1.621 + return NS_OK; 1.622 +} 1.623 + 1.624 +//----------------------------------------------------------------------------- 1.625 +// nsOfflineCacheUpdateService::nsIObserver 1.626 +//----------------------------------------------------------------------------- 1.627 + 1.628 +NS_IMETHODIMP 1.629 +nsOfflineCacheUpdateService::Observe(nsISupports *aSubject, 1.630 + const char *aTopic, 1.631 + const char16_t *aData) 1.632 +{ 1.633 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.634 + if (mUpdates.Length() > 0) 1.635 + mUpdates[0]->Cancel(); 1.636 + mDisabled = true; 1.637 + } 1.638 + 1.639 + if (!strcmp(aTopic, "disk-space-watcher")) { 1.640 + if (NS_LITERAL_STRING("full").Equals(aData)) { 1.641 + mLowFreeSpace = true; 1.642 + for (uint32_t i = 0; i < mUpdates.Length(); i++) { 1.643 + mUpdates[i]->Cancel(); 1.644 + } 1.645 + } else if (NS_LITERAL_STRING("free").Equals(aData)) { 1.646 + mLowFreeSpace = false; 1.647 + } 1.648 + } 1.649 + 1.650 + return NS_OK; 1.651 +} 1.652 + 1.653 +//----------------------------------------------------------------------------- 1.654 +// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService 1.655 +//----------------------------------------------------------------------------- 1.656 + 1.657 +static nsresult 1.658 +OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal, 1.659 + nsIPrefBranch *aPrefBranch, 1.660 + bool pinned, 1.661 + bool *aAllowed) 1.662 +{ 1.663 + *aAllowed = false; 1.664 + 1.665 + if (!aPrincipal) 1.666 + return NS_ERROR_INVALID_ARG; 1.667 + 1.668 + nsCOMPtr<nsIURI> uri; 1.669 + aPrincipal->GetURI(getter_AddRefs(uri)); 1.670 + 1.671 + if (!uri) 1.672 + return NS_OK; 1.673 + 1.674 + nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri); 1.675 + if (!innerURI) 1.676 + return NS_OK; 1.677 + 1.678 + // only http and https applications can use offline APIs. 1.679 + bool match; 1.680 + nsresult rv = innerURI->SchemeIs("http", &match); 1.681 + NS_ENSURE_SUCCESS(rv, rv); 1.682 + 1.683 + if (!match) { 1.684 + rv = innerURI->SchemeIs("https", &match); 1.685 + NS_ENSURE_SUCCESS(rv, rv); 1.686 + if (!match) { 1.687 + return NS_OK; 1.688 + } 1.689 + } 1.690 + 1.691 + nsAutoCString domain; 1.692 + rv = innerURI->GetAsciiHost(domain); 1.693 + NS_ENSURE_SUCCESS(rv, rv); 1.694 + 1.695 + if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) { 1.696 + *aAllowed = true; 1.697 + return NS_OK; 1.698 + } 1.699 + 1.700 + nsCOMPtr<nsIPermissionManager> permissionManager = 1.701 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.702 + if (!permissionManager) { 1.703 + return NS_OK; 1.704 + } 1.705 + 1.706 + uint32_t perm; 1.707 + const char *permName = pinned ? "pin-app" : "offline-app"; 1.708 + permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm); 1.709 + 1.710 + if (perm == nsIPermissionManager::ALLOW_ACTION || 1.711 + perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) { 1.712 + *aAllowed = true; 1.713 + } 1.714 + 1.715 + // offline-apps.allow_by_default is now effective at the cache selection 1.716 + // algorithm code (nsContentSink). 1.717 + 1.718 + return NS_OK; 1.719 +} 1.720 + 1.721 +NS_IMETHODIMP 1.722 +nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal, 1.723 + nsIPrefBranch *aPrefBranch, 1.724 + bool *aAllowed) 1.725 +{ 1.726 + return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed); 1.727 +} 1.728 + 1.729 +NS_IMETHODIMP 1.730 +nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI, 1.731 + nsIPrefBranch *aPrefBranch, 1.732 + bool *aAllowed) 1.733 +{ 1.734 + nsCOMPtr<nsIPrincipal> principal; 1.735 + nsContentUtils::GetSecurityManager()-> 1.736 + GetNoAppCodebasePrincipal(aURI, getter_AddRefs(principal)); 1.737 + return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed); 1.738 +} 1.739 + 1.740 +nsresult 1.741 +nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI, 1.742 + nsIPrefBranch *aPrefBranch, 1.743 + bool *aPinned) 1.744 +{ 1.745 + nsCOMPtr<nsIPrincipal> principal; 1.746 + nsContentUtils::GetSecurityManager()-> 1.747 + GetNoAppCodebasePrincipal(aDocumentURI, getter_AddRefs(principal)); 1.748 + return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned); 1.749 +} 1.750 + 1.751 +NS_IMETHODIMP 1.752 +nsOfflineCacheUpdateService::AllowOfflineApp(nsIDOMWindow *aWindow, 1.753 + nsIPrincipal *aPrincipal) 1.754 +{ 1.755 + nsresult rv; 1.756 + 1.757 + if (GeckoProcessType_Default != XRE_GetProcessType()) { 1.758 + TabChild* child = TabChild::GetFrom(aWindow); 1.759 + NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); 1.760 + 1.761 + if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) { 1.762 + return NS_ERROR_FAILURE; 1.763 + } 1.764 + 1.765 + nsAutoCString domain; 1.766 + rv = aPrincipal->GetBaseDomain(domain); 1.767 + NS_ENSURE_SUCCESS(rv, rv); 1.768 + 1.769 + nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain); 1.770 + } 1.771 + else { 1.772 + nsCOMPtr<nsIPermissionManager> permissionManager = 1.773 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.774 + if (!permissionManager) 1.775 + return NS_ERROR_NOT_AVAILABLE; 1.776 + 1.777 + rv = permissionManager->AddFromPrincipal( 1.778 + aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION, 1.779 + nsIPermissionManager::EXPIRE_NEVER, 0); 1.780 + NS_ENSURE_SUCCESS(rv, rv); 1.781 + } 1.782 + 1.783 + return NS_OK; 1.784 +}