uriloader/prefetch/nsOfflineCacheUpdateService.cpp

changeset 0
6474c204b198
     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 +}

mercurial